6 Praktik Keamanan Terbaik untuk Pergi

Terjemahan artikel disiapkan khusus untuk siswa kursus Pengembang Golang .





Popularitas Golang telah meningkat secara signifikan dalam beberapa tahun terakhir. Proyek yang sukses seperti Docker , Kubernetes dan Terraform telah membuat taruhan besar pada bahasa pemrograman ini. Go baru-baru ini menjadi standar de facto untuk membuat alat baris perintah. Dalam hal keamanan, Go telah berhasil mengelola sesuai dengan laporan kerentanannya, hanya memiliki satu registri CVE sejak tahun 2002 .

Namun, tidak adanya kerentanan tidak berarti bahwa bahasa pemrogramannya sangat aman. Kita manusia dapat membuat aplikasi yang tidak aman jika kita tidak mengikuti aturan tertentu. Misalnya, mengetahui aturan untuk menulis kode aman dari OWASP, kita dapat memikirkan cara menerapkan metode ini saat menggunakan Go. Dan inilah yang akan saya lakukan saat ini. Dalam posting ini, saya akan menunjukkan kepada Anda enam praktik untuk dipertimbangkan saat berkembang bersama Go.

1. Periksa input


Memeriksa input pengguna diperlukan tidak hanya untuk tujuan fungsional, tetapi juga membantu menghindari serangan oleh penjahat cyber yang mengirimi kami data mengganggu yang dapat merusak sistem. Selain itu, Anda membantu pengguna untuk menggunakan alat ini dengan lebih percaya diri, mencegah mereka membuat kesalahan konyol dan umum. Misalnya, Anda dapat mencegah pengguna mencoba menghapus beberapa entri sekaligus.

Untuk menguji input pengguna, Anda dapat menggunakan paket Go asli seperti strconv untuk menangani konversi string ke tipe data lainnya. Go juga mendukung ekspresi reguler dengan regexpuntuk pemeriksaan kompleks. Meskipun Go lebih suka menggunakan perpustakaan asli, ada paket pihak ketiga seperti validator. Dengan validator, Anda dapat mengaktifkan validasi untuk struktur atau bidang individual dengan lebih mudah. Misalnya, kode berikut memverifikasi bahwa struktur tersebut Userberisi alamat email yang valid:

package main

import (
	"fmt"

	"gopkg.in/go-playground/validator.v9"
)

type User struct {
	Email string `json:"email" validate:"required,email"`
	Name  string `json:"name" validate:"required"`
}

func main() {
	v := validator.New()
	a := User{
		Email: "a",
	}

	err := v.Struct(a)

	for _, e := range err.(validator.ValidationErrors) {
		fmt.Println(e)
	}
}


2. Gunakan template HTML


Satu kerentanan kritis yang umum adalah skrip lintas situs atau XSS. Eksploitasi utama adalah penyerang dapat menyuntikkan kode berbahaya ke dalam aplikasi untuk memodifikasi output. Misalnya, seseorang mungkin mengirim kode JavaScript sebagai bagian dari string kueri dalam URL. Ketika aplikasi mengembalikan nilai pengguna, kode JavaScript dapat dieksekusi. Karena itu, sebagai pengembang, Anda harus mengetahui kemungkinan ini dan menghapus data yang dimasukkan oleh pengguna.

Go memiliki paket html / templat untuk menyandikan apa yang dikembalikan aplikasi kepada pengguna. Jadi, bukannya browser yang melakukan input suka<script>alert(‘ !’);</script>dia akan memberi peringatan; Anda dapat menyandikan input, dan aplikasi akan memproses input sebagai kode HTML khas yang dicetak di browser. Server HTTP yang mengembalikan templat HTML akan terlihat seperti ini:

package main

import (
	"html/template"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	param1 := r.URL.Query().Get("param1")
	tmpl := template.New("hello")
	tmpl, _ = tmpl.Parse(`{{define "T"}}{{.}}{{end}}`)
	tmpl.ExecuteTemplate(w, "T", param1)
}
func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}


Tetapi ada perpustakaan pihak ketiga yang dapat Anda gunakan saat mengembangkan aplikasi web di Go. Misalnya, perangkat web Gorilla , yang mencakup perpustakaan untuk membantu pengembang melakukan hal-hal seperti menyandikan nilai otentikasi cookie. Ada juga nosurf , yang merupakan paket HTTP yang membantu mencegah pemalsuan permintaan lintas situs ( CSRF ).

3. Lindungi diri Anda dari injeksi SQL


Jika Anda telah mengembangkan selama beberapa waktu, Anda mungkin tahu tentang suntikan SQL, yang masih menempati posisi pertama di bagian atas OWASP . Namun, ada beberapa poin spesifik yang harus Anda pertimbangkan saat menggunakan Go. Hal pertama yang perlu Anda lakukan adalah memastikan bahwa pengguna yang terhubung ke database memiliki hak terbatas. Ini juga merupakan praktik yang baik untuk membersihkan input pengguna seperti yang saya jelaskan di bagian sebelumnya, atau untuk menghindari karakter khusus dan menggunakan fungsi HTMLEscapeString dari paket template HTML.

Tetapi bagian terpenting dari kode yang perlu Anda sertakan adalah penggunaan kueri yang diparameterisasi. Di Go, Anda tidak menyiapkan ekspresi (ini adalah Pernyataan Disiapkan) dalam koneksi; Anda mempersiapkannya di database. Berikut ini adalah contoh cara menggunakan kueri berparameter:

customerName := r.URL.Query().Get("name")
db.Exec("UPDATE creditcards SET name=? WHERE customerId=?", customerName, 233, 90)


Namun, bagaimana jika mesin basis data tidak mendukung penggunaan ekspresi yang disiapkan? Atau bagaimana jika itu memengaruhi kinerja kueri? Nah, Anda dapat menggunakan fungsi db.Query (), tetapi pertama-tama pastikan Anda membersihkan input pengguna, seperti yang ditunjukkan pada bagian sebelumnya. Ada juga perpustakaan pihak ketiga, seperti sqlmap , untuk mencegah injeksi SQL.

Terlepas dari semua upaya kami, kadang-kadang kerentanan tergelincir atau masuk ke aplikasi kami melalui pihak ketiga. Untuk melindungi aplikasi web Anda dari serangan kritis seperti injeksi SQL, pertimbangkan untuk menggunakan platform manajemen keamanan aplikasi seperti Sqreen .

4. Enkripsi informasi sensitif


Fakta bahwa string sulit dibaca, misalnya, dalam format basis-64, tidak berarti bahwa nilai tersembunyi adalah rahasia. Anda memerlukan cara untuk mengenkripsi informasi yang tidak mudah didekodekan oleh penyerang. Informasi khas yang ingin Anda enkripsi adalah kata sandi basis data, kata sandi pengguna, atau bahkan nomor jaminan sosial.

OWASP memiliki beberapa rekomendasi tentang apa yang digunakan algoritma enkripsi, misalnya, bcrypt, PDKDF2, Argon2atau scrypt. Untungnya, ada paket Go yang mencakup implementasi yang dapat diandalkan untuk mengenkripsi informasi - crypto . Kode berikut adalah contoh penggunaan bcrypt:

package main

import (
	"database/sql"
	"context"
	"fmt"

	"golang.org/x/crypto/bcrypt"
)

func main() {
	ctx := context.Background()
	email := []byte("john.doe@somedomain.com")
	password := []byte("47;u5:B(95m72;Xq")

	hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
	if err != nil {
		panic(err)
	}

	stmt, err := db.PrepareContext(ctx, "INSERT INTO accounts SET hash=?, email=?")
	if err != nil {
		panic(err)
	}
	result, err := stmt.ExecContext(ctx, hashedPassword, email)
	if err != nil {
		panic(err)
	}
}


Harap dicatat bahwa Anda masih perlu berhati-hati saat mentransfer informasi antar layanan. Anda tidak ingin mengirim data pengguna dalam teks biasa. Tidak masalah jika aplikasi mengenkripsi data pengguna sebelum menyimpan. Misalkan seseorang di Internet dapat mendengarkan lalu lintas Anda dan menyimpan catatan permintaan sistem Anda. Seorang penyerang dapat menggunakan informasi ini untuk mencocokkannya dengan data lain dari sistem lain.

5. Menyediakan konektivitas HTTPS


Sebagian besar browser saat ini membutuhkan HTTPS untuk bekerja di setiap situs. Chrome, misalnya, akan menampilkan peringatan jika situs tidak menggunakan HTTPS. Departemen keamanan informasi dapat menggunakan enkripsi transit untuk komunikasi antara layanan sebagai kebijakan. Yaitu, untuk memastikan keamanan koneksi transit dalam sistem, ini bukan hanya tentang mendengarkan aplikasi pada port 443. Anda juga perlu menggunakan sertifikat yang sesuai dan menggunakan HTTPS untuk mencegah penyerang menurunkan protokol ke HTTP.

Berikut ini cuplikan kode untuk aplikasi web yang menyediakan dan menggunakan protokol HTTPS:

package main

import (
    "crypto/tls"
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
        w.Write([]byte("This is an example server.\n"))
    })
    cfg := &tls.Config{
        MinVersion:               tls.VersionTLS12,
        CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
        PreferServerCipherSuites: true,
        CipherSuites: []uint16{
            tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
            tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
            tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
            tls.TLS_RSA_WITH_AES_256_CBC_SHA,
        },
    }
    srv := &http.Server{
        Addr:         ":443",
        Handler:      mux,
        TLSConfig:    cfg,
        TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
    }
    log.Fatal(srv.ListenAndServeTLS("tls.crt", "tls.key"))
}


Perhatikan bahwa aplikasi akan mendengarkan pada port 443. Baris berikutnya adalah baris yang menyediakan konfigurasi HTTPS:

w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")


Anda juga dapat menentukan nama server dalam konfigurasi TLS dengan cara ini:
config := &tls.Config{ServerName: "yourSiteOrServiceName"}


Selalu disarankan agar Anda menggunakan enkripsi back-end, bahkan jika aplikasi web Anda hanya untuk komunikasi internal. Bayangkan jika karena alasan tertentu penyerang dapat mendengarkan lalu lintas internal Anda. Kapan pun memungkinkan, selalu terbaik untuk meningkatkan standar bagi kemungkinan penyerang di masa depan.

6. Perhatikan kesalahan dan log.


Terakhir, namun tidak kalah pentingnya, penanganan kesalahan dan masuk dalam aplikasi Go Anda.

Untuk berhasil memecahkan masalah produksi, Anda perlu mengkonfigurasi aplikasi Anda dengan benar. Anda harus memperhitungkan kesalahan yang Anda tunjukkan kepada pengguna. Anda tidak ingin pengguna tahu apa yang salah. Penyerang dapat menggunakan informasi ini untuk menentukan layanan dan teknologi apa yang Anda gunakan. Selain itu, Anda harus ingat bahwa meskipun log itu indah, mereka disimpan di suatu tempat. Dan jika log jatuh ke tangan yang salah, mereka dapat digunakan untuk menanamkan serangan yang akan datang dalam sistem.

Jadi, hal pertama yang perlu Anda pelajari atau ingat adalah bahwa tidak ada pengecualian untuk Go. Ini berarti bahwa Anda perlu menangani kesalahan secara berbeda dari pada bahasa lain. Standarnya terlihat seperti ini:

if err != nil {
    //  
}


Go juga menawarkan perpustakaan bawaan untuk bekerja dengan log. Kode paling sederhana terlihat seperti ini:

package main

import (
	"log"
)

func main() {
	log.Print("Logging in Go!")
}


Tetapi ada perpustakaan pihak ketiga untuk memelihara log. Beberapa di antaranya adalah logrus, glogdan loggo. Berikut ini adalah potongan kecil kode yang menggunakan logrus:

package main

import (
	"os"

	log "github.com/sirupsen/logrus"
)

func main() {
	file, err := os.OpenFile("info.log", os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}

	defer file.Close()

	log.SetOutput(file)
	log.SetFormatter(&log.JSONFormatter{})
	log.SetLevel(log.WarnLevel)

	log.WithFields(log.Fields{
		"animal": "walrus",
		"size":   10,
	}).Info("A group of walrus emerges from the ocean")
}


Terakhir, pastikan Anda menerapkan semua rekomendasi sebelumnya, seperti enkripsi dan sanitasi data yang Anda masukkan ke dalam log.

Selalu ada ruang untuk tumbuh


Pedoman ini adalah minimum yang harus spesifik untuk aplikasi Go Anda. Namun, jika aplikasi Anda adalah alat baris perintah, Anda tidak akan memerlukan metode enkripsi untuk transit. Tetapi tip keamanan lainnya berlaku untuk hampir semua jenis aplikasi. Jika Anda ingin tahu lebih banyak, ada buku dari OWASP khusus untuk Go yang membahas beberapa topik. Ada juga repositori yang berisi tautan ke kerangka kerja, pustaka, dan alat keamanan lainnya di Go.

Apa pun yang Anda lakukan, ingatlah bahwa Anda selalu dapat meningkatkan keamanan aplikasi Anda. Penyerang akan selalu menemukan cara baru untuk mengeksploitasi kerentanan, jadi cobalah untuk terus bekerja pada keamanan aplikasi Anda.


. — , , , .NET, Node.js Java, Docker.

.

All Articles