A BLOG POST
Advisory Program 2022 (Chapter 3) - Securing Our Application
Tulisan ini adalah bagian dari serangkaian materi dari Advisory Program BCA, untuk source code nya bisa dilihat di sini.
Aplikasi kita saat ini sudah dapat digunakan, namun semua orang dapat melihat dan mengubah data. Padahal seharusnya, item to-do adalah milik orang per orang. Bagaimana mengatasinya? Pada bagian ini, kita akan menambahkan fungsi autentikasi dan otorisasi pada aplikasi kita, serta melakukan skenario penetration testing sederhana.
8/16/2024, 4:12:57 AM
89 times
Untuk menambahkan fitur authentication & authorization, kita perlu terlebih dahulu melakukan modifikasi pada database kita. Pertama, tabel todo
kita belum memiliki field untuk menampung informasi pembuat (created_by
) dan pengubah (modified_by
) data. Kedua field ini akan kita isi dengan informasi user yang akan diambil dari tabel users
. Data user ini juga akan kita gunakan untuk fitur login ke aplikasi sebelum aplikasi dapat digunakan. Dengan mekanisme seperti ini, skenario otentikasi dan otorisasi aplikasi yang kita harapkan akan menjadi sebagai berikut.
- User harus melakukan login terlebih dahulu sebelum dapat melihat, menambah dan mengubah data.
- Data yang dilihat, ditambah dan diubah adalah data spesifik per user.
- User tidak dapat melihat, mengubah atau menambah data milik/untuk user lain.
Dengan desain tersebut, yuk pertama ubah tabel todo
kita dengan menambahkan kolom created_by
dan updated_by
.
Todo.java
1@Entity
2@Table(name = "todo")
3@Data
4@ToString
5@EqualsAndHashCode
6@NoArgsConstructor
7public class Todo {
8
9 @Id
10 @GeneratedValue(generator = "UUID")
11 @Type(type = "pg-uuid")
12 @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
13 @Column(name = "id", updatable = false, nullable = false)
14 private UUID id;
15
16 @Column(name = "title")
17 private String title;
18
19 @Column(name = "created_date")
20 @ColumnDefault("NOW()")
21 private Date createdDate;
22
23 @Column(name = "modified_date")
24 private Date modifiedDate;
25
26 @Column(name = "is_done")
27 private boolean isDone;
28
29 @Column(name = "created_by")
30 private String createdBy;
31
32 @Column(name = "modified_by")
33 private String modifiedBy;
34
35 public Todo(String title, String createdBy) {
36 this.title = title;
37 this.createdBy = createdBy;
38 this.createdDate = Calendar.getInstance().getTime();
39 }
40}
Kita juga perlu membuat tabel users
dengan desain berikut.
User.java
1@Entity
2@Table(name = "users")
3@Data
4@EqualsAndHashCode
5@ToString
6@NoArgsConstructor
7public class User {
8
9 @Id
10 @GeneratedValue(generator = "UUID")
11 @Type(type = "pg-uuid")
12 @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
13 @Column(name = "id", updatable = false, nullable = false)
14 private UUID id;
15
16 @Column(name = "username", nullable = false)
17 private String username;
18
19 @Column(name = "name", nullable = false)
20 private String name;
21
22 @Column(name = "hash_password", nullable = false)
23 private String password;
24
25 public User(String username, String name, String password) {
26 this.username = username;
27 this.name = name;
28 this.password = password;
29 }
30}
Kemudian kita lengkapi dahulu fungsionalitas dari entitas User
dengan fungsi berikut.
UserRepository.java
1public interface UserRepository extends JpaRepository<User, String> {
2 Optional<User> findById(UUID id);
3
4 Optional<User> findByUsernameAndPassword(String username, String password);
5
6 Optional<User> findByUsername(String username);
7}
- Fungsi
findById
akan digunakan untuk melakukan pengecekan apakah user terdaftar / tidak melaluiID
yang dikirimkan. - Fungsi
findByUsername
akan digunakan untuk pencarian data user berdasarkan username tertentu. Hal ini bisa kita gunakan untuk pengecekan apakah user dengan username yang dimaksud terdaftar atau tidak, serta proses pendaftaran user agar tidak ada username yang sama. - Fungsi
findByUsernameAndPassword
digunakan untuk fungsi login dengan mencocokan username dengan password yang dikirim.
Kita tambahkan fungsi registrasi user pada service dan controller layer dari entitas User sebagai berikut.
UserService.java
1@Service
2public class UserService {
3
4 @Autowired
5 UserRepository userRepo;
6
7 public String getUserIdByUsernameAndPassword(String username, String password) {
8 String userId = null;
9 Optional<User> _user = userRepo.findByUsernameAndPassword(username, MD5Crypto.hash(password));
10 if (_user.isPresent()) {
11 userId = _user.get().getId().toString();
12 }
13 return userId;
14 }
15
16 public User getUserById(String id) {
17 Optional<User> _user = userRepo.findById(UUID.fromString(id));
18 if (_user.isPresent())
19 return _user.get();
20 else
21 return null;
22 }
23
24}
Nah, mari kita coba untuk membuat data user.
Setelah fungsional modul User berjalan dengan baik, saatnya kita lakukan modifikasi ke fitur Todo kita untuk dimanfaatkan sebagai otentikasi dan otorisasi.
Modifikasi yang akan kita lakukan pada API Todo kita adalah deteksi user yang mengakses/requester untuk mengetahui apakah request tersebut eligible/tidak. Pertanyaannya, melalui apa akan kita terima informasi tersebut? Apakah melalui request body? Sebenarnya sah-sah saja kita lakukan dengan cara itu, tapi perlu kita ingat bahwa komponen dalam HTTP network ada banyak.
Salah satu common practice yang digunakan untuk tujuan otorisasi seperti ini biasanya dilakukan melalui komponen Header, menggunakan key Authorization
. Cara ini juga memberikan benefit bagi kita untuk tidak menerima request yang tidak perlu melalui payload request body.
Untuk mendapatkan payload pada HTTP Header, Java telah menyediakan interface yang dapat digunakan yaitu HttpServletRequest
. Dengan demikian, kita akan mem-validasi terlebih dahulu Authorization
key pada header, dan mencocokannya ke dalam data user.
Dengan ditambahkannya pengecekan ini, tentu kita juga perlu memodifikasi request payload kita agar dapat berjalan lancar. Melalui Postman, kita dapat menambahkan HTTP Header seperti ini.
Tambahkan beberapa data contoh dan lakukanlah tes hit dan dapat kita lihat bahwa API kita dapat berjalan dengan baik sesuai dengan skenario nomor 2, yaitu "Data yang dilihat, ditambah dan diubah adalah data spesifik per user".
Selanjutnya, kita juga perlu memodifikasi aplikasi Angular kita.
Dimulai dari yang paling mendasar dahulu, mari kita buat component untuk fungsi signin dan signup pada frontend.