Dalam beberapa tahun terakhir, Golang telah menyebar semakin luas. Proyek yang sukses seperti Docker, Kubernetes dan Terraform telah membuat taruhan besar pada bahasa pemrograman ini. Go telah menjadi standar de facto untuk membuat alat baris perintah. Dan jika kita berbicara tentang keamanan, ternyata di area ini di Go semuanya dalam urutan sempurna. Yaitu, sejak tahun 2002, hanya
satu kerentanan Golang telah dicatat dalam registri CVE.
Namun, fakta bahwa tidak ada kerentanan dalam bahasa pemrograman tidak berarti bahwa aplikasi apa pun yang ditulis dalam bahasa ini akan sepenuhnya aman. Jika pengembang tidak mematuhi rekomendasi tertentu, ia dapat membuat aplikasi yang tidak dilindungi bahkan dalam bahasa seperti itu. Dalam kasus Go, Anda dapat menemukan rekomendasi serupa dengan merujuk ke materi
OWASP .

Penulis artikel, terjemahan yang kami terbitkan hari ini, dirumuskan, berdasarkan data OWASP, 6 rekomendasi untuk mengembangkan aplikasi aman di Go.
1. Verifikasi input pengguna
Validasi data yang dimasukkan oleh pengguna diperlukan tidak hanya untuk memastikan berfungsinya aplikasi dengan benar. Hal ini juga bertujuan untuk memerangi penyusup yang, menggunakan data yang disiapkan dengan cara khusus, mencoba mengganggu sistem. Selain itu, memeriksa data pengguna membantu pengguna untuk bekerja dengan aplikasi lebih percaya diri, karena melindungi mereka dari kesalahan umum. Misalnya, dengan menganalisis perintah pengguna, Anda dapat mencegah upaya untuk menghapus beberapa catatan pada saat yang sama dalam situasi di mana tindakan seperti itu dapat menyebabkan tidak berfungsinya sistem.
Anda dapat menggunakan paket Go standar untuk memverifikasi input pengguna. Sebagai contoh, paket
strconv
membantu mengonversi data string ke tipe data lainnya. Go juga mendukung, terima kasih kepada
regexp
, ekspresi reguler. Mereka dapat digunakan untuk mengimplementasikan skrip validasi data yang kompleks. Terlepas dari kenyataan bahwa dalam lingkungan pengembangan preferensi Go biasanya diberikan ke perpustakaan standar, ada paket pihak ketiga yang bertujuan memeriksa data. Misalnya,
validator . Paket ini memudahkan untuk memverifikasi struktur data yang kompleks atau nilai tunggal. Misalnya, kode berikut memeriksa struktur
User
untuk kebenaran alamat email yang terkandung di dalamnya:
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
XSS (skrip lintas situs, skrip lintas situs) adalah kerentanan serius dan luas. Kerentanan XSS memungkinkan penyerang untuk menyuntikkan kode berbahaya ke dalam aplikasi yang dapat memengaruhi data yang dihasilkan oleh aplikasi tersebut. Misalnya, seseorang dapat mengirim kode JavaScript ke aplikasi, sebagai bagian dari string kueri dalam URL. Ketika aplikasi memproses permintaan semacam itu, kode JavaScript ini dapat dieksekusi. Akibatnya, ternyata pengembang aplikasi harus mengharapkan ini dan membersihkan data yang diterima dari pengguna.
Go memiliki paket
html / templat yang memungkinkan Anda membuat kode HTML yang dilindungi dari fragmen berbahaya. Akibatnya, peramban yang menampilkan aplikasi yang diserang, alih-alih mengeksekusi kode seperti
<script>alert('You've Been Hacked!');</script>
, yang memberi tahu pengguna bahwa ia diretas, akan menganggap kode JavaScript berbahaya sebagai teks biasa . Inilah tampilan server HTTP yang menggunakan templat HTML:
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) }
Ada perpustakaan pihak ketiga yang dapat Anda gunakan saat mengembangkan aplikasi web di Go. Katakanlah ini adalah perangkat
web Gorilla . Toolkit ini mencakup perpustakaan yang membantu pengembang, misalnya, menyandikan nilai dalam cookie otentikasi. Dan di sini ada proyek lain -
nosurf . Ini adalah paket HTTP yang membantu mencegah serangan
CSRF .
3. Lindungi proyek Anda dari injeksi SQL
Jika Anda bukan orang baru dalam pengembangan web, maka Anda mungkin tahu tentang serangan injeksi SQL (menggunakan metode menyuntikkan kode SQL sewenang-wenang ke dalam kueri). Kerentanan yang sesuai masih menempati peringkat pertama dalam peringkat
OWASP Top 10 . Untuk melindungi aplikasi dari injeksi SQL, Anda perlu mempertimbangkan beberapa fitur. Jadi, hal pertama yang harus dipastikan adalah bahwa pengguna yang terhubung ke database akan memiliki hak istimewa yang terbatas. Selain itu, disarankan untuk menghapus data yang dimasukkan oleh pengguna, seperti yang telah kami sebutkan, atau untuk menghindari karakter khusus dan menggunakan fungsi
HTMLEscapeString dari paket
html/template
.
Tetapi hal yang paling penting dalam melindungi terhadap injeksi SQL adalah penggunaan query parameterized (disiapkan ekspresi). Di Go, ekspresi tidak disiapkan untuk koneksi, tetapi untuk database. Berikut ini adalah contoh penggunaan kueri parameterisasi:
customerName := r.URL.Query().Get("name") db.Exec("UPDATE creditcards SET name=? WHERE customerId=?", customerName, 233, 90)
Tetapi bagaimana jika mesin database tidak mendukung penggunaan ekspresi yang disiapkan sebelumnya? Tetapi bagaimana jika ini mempengaruhi kinerja permintaan? Dalam kasus seperti itu, Anda dapat menggunakan fungsi
db.Query()
, tetapi pertama-tama Anda harus ingat untuk menghapus input pengguna. Untuk mencegah serangan menggunakan injeksi SQL, Anda bisa menggunakan perpustakaan pihak ketiga, seperti
sqlmap .
Perlu dicatat bahwa, terlepas dari semua upaya untuk melindungi aplikasi dari serangan SQL, terkadang penyerang masih berhasil dalam serangan ini. Katakan - melalui dependensi aplikasi eksternal. Untuk meningkatkan tingkat keamanan proyek, Anda dapat menggunakan alat yang sesuai untuk memverifikasi keamanan aplikasi. Misalnya, alat dari platform
Sqreen .
4. Enkripsi informasi penting
Jika seseorang tidak dapat membaca string tertentu, katakanlah, dalam penyandian BASE64, ini tidak berarti bahwa informasi yang tersembunyi di dalamnya dilindungi dengan andal. Oleh karena itu, informasi penting harus dienkripsi, melindunginya dari penyusup yang dapat memperoleh akses ke data terenkripsi. Biasanya informasi yang dienkripsi seperti kata sandi basis data, kata sandi pengguna, data pribadi pengguna.
Dalam kerangka proyek OWASP, beberapa
rekomendasi dibuat mengenai algoritma enkripsi yang disukai. Sebagai contoh, itu adalah
bcrypt
,
PDKDF2
,
Argon2
,
scrypt
. Ada paket Go,
crypto , yang berisi implementasi berbagai algoritma enkripsi yang andal. Berikut ini contoh menggunakan algoritma
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 meskipun menggunakan enkripsi, Anda harus menjaga keamanan transfer informasi antar layanan. Misalnya, Anda tidak boleh mentransfer kata sandi pengguna di suatu tempat dalam teks biasa, bahkan jika itu disimpan dalam bentuk terenkripsi. Saat mengirimkan data di Internet, ada baiknya mulai dari asumsi bahwa mereka dapat dicegat oleh penyerang yang mengumpulkan data dari permintaan yang dieksekusi dalam sistem tertentu. Penyerang dapat mencocokkan informasi yang dikumpulkan dengan data yang diterima dari sistem lain, dan sebagai hasilnya dapat memecahkan proyek yang menarik baginya.
5. Terapkan HTTPS
Saat ini, sebagian besar browser memerlukan situs yang terbuka dengan mereka untuk mendukung HTTPS. Chrome, misalnya, akan menampilkan pemberitahuan di bilah alamat jika data dipertukarkan dengan situs tanpa menggunakan HTTPS. Dalam organisasi yang mendukung proyek tertentu, kebijakan keamanan dapat diterapkan yang bertujuan mengatur pertukaran data yang aman antara layanan yang membentuk proyek ini. Akibatnya, untuk memastikan keamanan koneksi, perlu memperhatikan tidak hanya mendengarkan aplikasi pada port 443. Proyek ini harus memiliki sertifikat yang sesuai, perlu untuk mengatur penggunaan HTTPS secara paksa untuk mencegah penyerang beralih ke pertukaran data HTTP.
Berikut adalah contoh aplikasi yang memberlakukan 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")) }
Harap dicatat bahwa aplikasi mendengarkan pada port 443. Dan di sini adalah baris yang bertanggung jawab atas penggunaan HTTPS secara paksa:
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
Selain itu, menentukan nama server dalam konfigurasi TLS mungkin masuk akal:
config := &tls.Config{ServerName: "yourSiteOrServiceName"}
Dianjurkan agar Anda selalu menggunakan enkripsi data yang dikirim antara bagian-bagian sistem, bahkan jika aplikasi web itu sedikit dari perusahaan obrolan internal. Bayangkan konsekuensi yang mungkin timbul dari serangan di mana penyerang mencegat data aplikasi yang dikirimkan melalui jaringan. Bilamana mungkin, ada baiknya untuk mempersiapkan kemungkinan serangan terhadap proyek, berusaha untuk mempersulit kehidupan penyerang sebanyak mungkin.
6. Hati-hati tentang penanganan kesalahan dan pencatatan.
Item ini adalah yang terakhir dalam daftar kami, tetapi ini jelas jauh dari yang terakhir. Di sini kita berbicara tentang penanganan kesalahan dan pencatatan.
Untuk mengatasi masalah yang muncul dalam produksi tepat waktu, Anda harus melengkapi aplikasi dengan alat yang tepat. Pada saat yang sama, Anda harus sangat berhati-hati tentang pesan kesalahan yang ditampilkan kepada pengguna. Anda tidak boleh memberi tahu pengguna rincian tentang apa yang salah. Faktanya adalah bahwa penyerang dapat menggunakan informasi ini untuk mengetahui layanan dan teknologi apa yang digunakan dalam proyek. Selain itu, perlu diingat bahwa meskipun log biasanya dirasakan dalam cahaya positif, log ini disimpan di suatu tempat. Dan jika log aplikasi jatuh ke tangan yang salah, menganalisis informasi darinya dapat membantu dalam melakukan serangan terhadap proyek.
Hal pertama yang perlu dipertimbangkan di sini adalah tidak ada pengecualian untuk Go. Ini berarti bahwa kesalahan dalam aplikasi yang ditulis dalam Go tidak menangani kesalahan dengan cara yang sama seperti kesalahan dalam aplikasi yang ditulis dalam bahasa lain. Biasanya terlihat seperti ini:
if err != nil {
Selain itu, Go memiliki perpustakaan standar untuk bekerja dengan log yang disebut
log
. Berikut adalah contoh sederhana penggunaannya:
package main import ( "log" ) func main() { log.Print("Logging in Go!") }
Ada perpustakaan pihak ketiga untuk mengatur penebangan. Misalnya, ini adalah
logrus
,
glog
,
loggo
. Berikut adalah contoh kecil 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") }
Dan akhirnya, ketika bekerja dengan data yang masuk ke dalam log, gunakan rekomendasi keamanan yang telah kita bahas. Secara khusus, hapus dan enkripsi data tersebut.
Ringkasan
Rekomendasi yang diberikan di sini adalah minimum yang harus dimiliki oleh proyek yang ditulis dalam Go. Tetapi jika proyek tersebut adalah utilitas command-line, maka tidak perlu menerapkan perlindungan untuk lalu lintas yang dikirimkan melalui jaringan. Tips yang tersisa berlaku untuk hampir semua jenis aplikasi. Jika Anda ingin menjelajahi secara mendalam masalah pengembangan aplikasi aman di Go, lihat
buku OWASP tentang hal ini. Dan di
sini ada repositori yang berisi tautan ke berbagai alat yang ditujukan untuk memastikan keamanan aplikasi-Go.
Apa pun yang Anda lakukan di bidang pengamanan aplikasi Anda, ingatlah bahwa keamanan selalu dapat ditingkatkan dan bahwa penyerang terus-menerus menemukan cara baru untuk menyerang. Karena itu, perlindungan aplikasi adalah apa yang perlu Anda lakukan, bukan dari kasus ke kasus, tetapi terus-menerus.
Pembaca yang budiman! Bagaimana Anda melindungi aplikasi Anda yang ditulis dalam Go?
