Lewati ke konten utama

Cari di CooLWJ

选择搜索范围,输入关键词后按回车。

Bahasa

Seni enkripsi dan dekripsi

Satu Enkripsi simetris

Enkripsi simetris dan enkripsi asimetris merupakan dua metode enkripsi yang berbeda, dan keduanya berperan penting dalam keamanan data serta transmisi informasi. Berikut saya akan memperkenalkan kedua teknologi enkripsi ini secara terpisah:

1.1 Enkripsi simetris(enkripsi dengan kunci yang sama)

Enkripsi simetris berarti menggunakan kunci yang sama untuk enkripsi dan dekripsi. Ini berarti pengirim dan penerima harus sama-sama memiliki kunci tersebut agar dapat melakukan operasi enkripsi dan dekripsi.

Algoritma enkripsi simetris yang umum:

  • AES(Standar Enkripsi Lanjutan)

  • DES(Standar Enkripsi Data)

  • 3DES(algoritma enkripsi data tiga kali)

1.2 Karakteristik enkripsi simetris

Secara umum, enkripsi simetris memiliki karakteristik berikut:

  1. Relatif cepat:Karena enkripsi dan dekripsi menggunakan kunci yang sama, enkripsi simetris biasanya lebih cepat daripada enkripsi asimetris.

  2. Pengelolaan kunci: Tantangan utama enkripsi simetris terletak pada distribusi dan pengelolaan kunci. Jika kunci bocor, keamanan enkripsi akan terancam.

  3. Cocok untuk data dalam jumlah besar:Karena cepat, enkripsi simetris cocok untuk mengenkripsi data dalam jumlah besar.

Dari sini terlihat bahwa enkripsi simetris memiliki dua keunggulan utama: pertama, kecepatan komputasi yang tinggi; kedua, cocok untuk data dalam jumlah besar.

Namun, enkripsi simetris memiliki satu masalah fatal, yaitu pengelolaan kunci. Bagaimana mengirimkan kunci secara aman dari server ke klien merupakan persoalan! Selain itu, dalam komunikasi satu ke banyak, mengelola kunci agar tidak bocor juga menjadi tantangan. Inilah kekurangan enkripsi simetris.

Ilustrasi artistik dinding berwarna biru kehijauan dan rol cat

1.3 Contoh kode

Selanjutnya, Kak Song akan menunjukkan kepada kalian cara melakukan enkripsi dan dekripsi simetris dengan kode Java.

Untuk menerapkan enkripsi simetris di Java,biasanya digunakan kelas dan antarmuka yang disediakan oleh Arsitektur Kriptografi Java(Java Cryptography Architecture, JCA)。

Berikut contoh sederhana enkripsi dan dekripsi simetris menggunakan algoritma AES(Advanced Encryption Standard):

public class SymmetricEncryptionExample {    // 生成密钥
    public static SecretKey generateKey() throws Exception {     
       KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128); // 可以是128, 192或256位
        return keyGenerator.generateKey();
    }    
    // 加密方法
    public static String encrypt(String data, SecretKey key) throws Exception {      
      Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, key);       
         byte[] encryptedBytes = cipher.doFinal(data.getBytes());        
         return Base64.getEncoder().encodeToString(encryptedBytes);
    }    
    // 解密方法
    public static String decrypt(String encryptedData, SecretKey key) throws Exception {        
    Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, key);       
         byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));       
          return new String(decryptedBytes);
    }    
    public static void main(String[] args) {        
    try {           
             // 生成密钥
            SecretKey key = generateKey();           
             // 原始数据
            String originalData = "Hello, JavaBoy!";            
            // 加密
            String encryptedData = encrypt(originalData, key);
            System.out.println("加密数据: " + encryptedData);            
            // 解密
            String decryptedData = decrypt(encryptedData, key);
            System.out.println("解密数据: " + decryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. Menghasilkan kunci:menggunakan KeyGenerator menghasilkan sebuah kunci AES.

  2. Enkripsi:menggunakan Cipher kelas untuk mengenkripsi,mengubah data menjadi byte sebelum mengenkripsinya,lalu menggunakan pengodean Base64 untuk mengubahnya menjadi string,agar mudah disimpan atau dikirim。

  3. Dekripsi:Dekodekan string terenkripsi kembali menjadi byte, lalu dekripsi menggunakan kunci yang sama.

Hal yang perlu diperhatikan dari kode di atas adalah:

  • Panjang kunci(misalnya 128 bit)harus dipilih berdasarkan kebutuhan keamanan。

  • Pastikan kunci disimpan dengan aman, jangan menanamkan kunci secara langsung dalam kode.

  • Untuk lingkungan produksi, pertimbangkan penggunaan strategi pengelolaan kunci yang lebih aman.

Demi keamanan, kita biasanya menggunakan metode di atas untuk menghasilkan kunci. Salah satu ciri kunci yang dihasilkan dengan metode ini adalah kunci akan berubah setiap kali sistem dimulai ulang. Jika Anda ingin mengendalikan sendiri pembuatan kunci, Anda dapat menghasilkan kunci dengan cara berikut:

public static SecretKey generateKeyFromPassword(String password, int keySize) throws NoSuchAlgorithmException, InvalidKeySpecException {   
  SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");   
   KeySpec spec = new PBEKeySpec(password.toCharArray(), "salt".getBytes(), 65536, keySize);   
   return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
}

Dengan demikian, kunci dapat dikendalikan melalui parameter yang Anda masukkan sendiri.

1.4 Integrasi frontend dan backend

Saat menggunakan algoritma AES untuk enkripsi dan dekripsi lintas bahasa (seperti JavaScript dan Java), hal utama adalah memastikan kedua sisi menggunakan kunci yang sama, mode algoritma yang sama (seperti CBC, ECB, dan sebagainya), mode padding yang sama (seperti PKCS5Padding, PKCS7Padding, dan sebagainya), serta vektor inisialisasi yang sama (IV, jika menggunakan mode yang membutuhkan IV seperti CBC).

Sebelumnya ada teman yang mengatakan bahwa setelah mengenkripsi di frontend, backend selalu gagal mendekripsinya. Di sini, Kak Song juga memberikan contoh yang memadukan frontend dan backend.

Enkripsi di frontend, dekripsi di backend

Kode frontend:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>AES加密示例</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
    </head>
    <body>
    <script>
    function encryptAES(text, secretKey) {        
    const key = CryptoJS.enc.Utf8.parse(secretKey);        
    const iv = CryptoJS.lib.WordArray.random(128 / 8); 
    // 对于CBC模式,需要IV
    
        const encrypted = CryptoJS.AES.encrypt(text, key, {           
         iv: iv,           
          mode: CryptoJS.mode.CBC,            
          padding: CryptoJS.pad.Pkcs7
        });        
        // 返回加密后的文本和IV(Base64格式),实际使用中可能需要安全地传输这些值
        return {          
          ciphertext: encrypted.toString(),          
            iv: iv.toString(CryptoJS.enc.Base64)
        };
    }    
    const secretKey = 'helloworldhelloworldhelloworld11'; /
    / 确保密钥是32个字符长(256位)
    const text = 'Hello, javaboy!';    
    const result = encryptAES(text, secretKey);    
    console.log(result);
    </script>
    </body>
    </html>


Kode backend:

import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;public class AESDecrypt {    public static String decryptAES(String encryptedData, String secretKey, String iv) throws Exception {        IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));        SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES");        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);        byte[] decodedValue = Base64.getDecoder().decode(encryptedData);        byte[] decryptedValue = cipher.doFinal(decodedValue);        return new String(decryptedValue, "UTF-8");
    }    public static void main(String[] args) {        try {            //前端加密后的文本
            String encryptedText = "PYANpAjMsRnBIEhovtEXQw==";            String secretKey = "helloworldhelloworldhelloworld11";            //前端 IV,要和加密后的文本一起传到后端
            String iv = "y/jUHcgSOpOiyNlsfjNUBg==";            String decryptedText = decryptAES(encryptedText, secretKey, iv);
            System.out.println("解密文本: " + decryptedText);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Perhatikan bahwa enkripsi di frontend akan menghasilkan parameter iv, yang harus dikirim ke backend bersama hasil enkripsi frontend.

Enkripsi di backend, dekripsi di frontend

Saat melakukan enkripsi AES di Java dan mendekripsinya di JavaScript, kedua sisi juga harus dipastikan menggunakan kunci yang sama, mode algoritma yang sama(seperti CBC, ECB, dan sebagainya), mode padding yang sama(seperti PKCS5Padding, PKCS7Padding, dan sebagainya), serta(jika berlaku)vektor inisialisasi(IV)yang sama.

Kode backend:

public class AESEncrypt {    public static String encryptAES(String plainText, String secretKey, String iv) throws Exception {        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(256); // 对于AES-256
        SecretKey secret = new SecretKeySpec(secretKey.getBytes("UTF-8"), "AES");        IvParameterSpec ivParameterSpec = new IvParameterSpec(Base64.getDecoder().decode(iv));        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret, ivParameterSpec);        byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));        return Base64.getEncoder().encodeToString(encrypted);
    }    public static void main(String[] args) {        try {            String plainText = "Hello, 江南一点雨!";            String secretKey = "helloworldhelloworldhelloworld11"; // 确保密钥是32个字符长(256位)
            String iv = Base64.getEncoder().encodeToString(new byte[16]); // 示例IV,实际应用中应更安全地生成

            String encryptedText = encryptAES(plainText, secretKey, iv);
            System.out.println("Encrypted text: " + encryptedText);
            System.out.println("IV (Base64): " + iv); // 确保将IV发送给解密方

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Kode frontend:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
    <script>
        function decryptAES(ciphertext, secretKey, iv) {            const key = CryptoJS.enc.Utf8.parse(secretKey);            const ivParsed = CryptoJS.enc.Base64.parse(iv);            const decrypted = CryptoJS.AES.decrypt(
                {                    ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
                },
                key,
                {                    iv: ivParsed,                    mode: CryptoJS.mode.CBC,                    padding: CryptoJS.pad.Pkcs7
                }
            );            return decrypted.toString(CryptoJS.enc.Utf8);
        }        const secretKey = 'helloworldhelloworldhelloworld11';        const iv = 'AAAAAAAAAAAAAAAAAAAAAA=='; // 从Java代码获取
        const ciphertext = '1lPNVF1injas78KUWeKp5FusEr6f0pGgcrLAg9ELFr8='; // 从Java代码获取

        const decryptedText = decryptAES(ciphertext, secretKey, iv);        console.log(decryptedText);    </script></head><body></body></html>

Kode enkripsi dan dekripsi untuk interaksi frontend dan backend di atas sudah diuji langsung oleh Song Ge dan semuanya berjalan tanpa masalah. Jika kalian membutuhkan hal semacam ini, jangan lupa menyimpan artikel ini agar dapat dijadikan referensi.

Dua Enkripsi asimetris

2.1 Apa itu enkripsi asimetris

Enkripsi asimetris, yang juga disebut enkripsi kunci publik, adalah metode enkripsi yang menggunakan dua kunci berbeda(kunci publik dan kunci privat).

Secara umum, enkripsi asimetris memiliki beberapa karakteristik berikut:

  1. Kunci publik dan kunci privat

  • Pembuatan kunci publik dan kunci privat:Enkripsi asimetris menggunakan sepasang kunci. Kunci publik bersifat terbuka dan dapat diakses siapa saja, sedangkan kunci privat bersifat pribadi dan hanya dapat diakses oleh pemiliknya. Kedua kunci ini dihasilkan melalui algoritma matematika dan saling berkaitan, tetapi salah satunya tidak dapat diturunkan dari yang lain.

  1. Keamanan tinggi

  • Sulit dibobol:Karena hubungan matematis yang kompleks antara kunci publik dan kunci privat, enkripsi asimetris memiliki tingkat keamanan yang tinggi. Penyerang sangat sulit menyimpulkan kunci privat dari kunci publik, sehingga keamanan data terenkripsi terjamin.

  • Ketahanan terhadap serangan komputasi kuantum:Sejumlah proyek blockchain mulai menggunakan algoritma kriptografi yang tahan terhadap serangan komputasi kuantum, seperti varian aman kuantum dari Algoritma Tanda Tangan Digital Kurva Eliptik(ECDSA), untuk menghadapi ancaman komputasi kuantum di masa depan.

  1. Kunci publik mudah didistribusikan

  • Sifat publik kunci publik:Kunci publik dapat dibagikan kepada siapa saja, sehingga sangat mudah didistribusikan. Siapa pun dapat menggunakan kunci publik untuk mengenkripsi data, tetapi hanya pemegang kunci privat yang dapat mendekripsinya.

  1. Autentikasi identitas dan tanda tangan digital

  • Autentikasi identitas:Kunci publik dapat digunakan sebagai pengenal identitas pengguna,sehingga orang lain dapat memverifikasi identitas pengguna tanpa mengetahui kunci privatnya。Hal ini membantu membangun identitas tepercaya dalam jaringan terdesentralisasi seperti blockchain。

  • Tanda tangan digital:Pemegang kunci privat dapat menggunakan kunci privat untuk menandatangani pesan, sementara orang lain dapat menggunakan kunci publik untuk memverifikasi keaslian tanda tangan tersebut. Ini menjamin integritas pesan dan keandalan sumbernya.

  1. Komunikasi aman

  • Komunikasi terenkripsi: Kunci publik dapat digunakan untuk mengenkripsi pesan, dan hanya orang yang memiliki kunci privat yang sesuai yang dapat mendekripsinya. Hal ini menjamin keamanan data selama komunikasi dan mencegah informasi diakses oleh pihak yang tidak berwenang.

  1. Pengendalian aset digital

  • Alamat blockchain dan kunci privat:Alamat blockchain biasanya diturunkan dari kunci publik. Pengguna mengendalikan aset digital yang terkait dengan alamat tersebut melalui kunci privat. Keamanan kunci privat sangat penting bagi keamanan aset digital.

  1. Kecepatan enkripsi dan dekripsi

  • Relatif lebih lambat:kecepatan enkripsi dan dekripsi asimetris jauh lebih lambat dibandingkan enkripsi simetris karena memerlukan perhitungan matematika yang lebih rumit. Namun, hal ini tidak menghalangi penggunaannya dalam komunikasi aman, autentikasi identitas, dan bidang lainnya.

2.2 Kegunaan enkripsi asimetris

Enkripsi asimetris memiliki dua skenario penggunaan klasik.

  1. Enkripsi:Inilah penggunaan yang paling kita kenal,yaitu mengenkripsi dengan kunci publik,dan mendekripsi dengan kunci privat。

  2. Tanda tangan:Mengingat jaringan tidak dapat dipercaya dan data mungkin diubah selama transmisi, pada saat seperti ini kunci publik dan privat dapat digunakan dengan peran terbalik, yakni kunci privat untuk menandatangani data dan kunci publik untuk memverifikasi tanda tangan, guna memastikan keamanan dan keutuhan data.

Untuk kegunaan kedua, sebagian orang menyebutnya mengenkripsi dengan kunci privat dan mendekripsi dengan kunci publik; toh semua tahu bahwa yang dimaksud adalah hal yang sama.

Meskipun secara teori algoritma enkripsi asimetris dapat digunakan untuk enkripsi data dan tanda tangan digital, dalam praktiknya kompleksitas komputasi yang tinggi dan efisiensi yang rendah menjadi hambatan utama.

Dibandingkan algoritma enkripsi simetris,kecepatan komputasi enkripsi asimetris lebih lambat beberapa orde magnitudo,yang sangat memengaruhi kemampuannya menangani data dalam jumlah besar。Selain itu,karena proses enkripsi dan dekripsi algoritma enkripsi asimetris sangat berkaitan dengan panjang kunci,dan tidak mendukung mode enkripsi blok,algoritma ini hanya dapat menangani sejumlah kecil data yang tidak melebihi panjang kunci,sehingga tidak dapat mengenkripsi data dalam jumlah besar。

Untuk mengatasi kekurangan kinerja enkripsi asimetris, sistem enkripsi modern biasanya menggunakan strategi enkripsi hibrida, yaitu memadukan keunggulan enkripsi simetris dan asimetris. Dalam strategi ini, enkripsi asimetris terutama digunakan untuk mengirimkan kunci enkripsi simetris secara aman kepada pihak lain (yaitu “negosiasi kunci”). Setelah kedua pihak berbagi kunci simetris ini secara aman, algoritma enkripsi simetris yang efisien dapat digunakan untuk mengenkripsi dan mendekripsi data dalam jumlah besar. Metode gabungan ini meningkatkan efisiensi enkripsi sekaligus memperkuat keamanan komunikasi, dan digunakan secara luas dalam berbagai protokol komunikasi aman, seperti SSL/TLS.

Dalam bidang tanda tangan digital, beberapa penyesuaian juga dilakukan untuk meningkatkan efisiensi enkripsi asimetris. Caranya adalah terlebih dahulu memproses data asli menjadi ringkasan hash(dapat menggunakan MD5, SHA, dan sebagainya), sehingga diperoleh nilai hash dengan panjang tetap. Kemudian, algoritma enkripsi asimetris digunakan untuk mengenkripsi nilai hash tersebut dan menghasilkan tanda tangan digital. Karena algoritma hash dapat secara efisien memadatkan data dengan panjang berapa pun menjadi ringkasan dengan panjang tetap, panjang data hash akan selalu sama, seberapa pun besarnya data asli, sehingga proses penandatanganan tetap efisien. Saat memverifikasi tanda tangan, cukup hitung ulang hash data asli dan bandingkan dengan tanda tangan yang telah didekripsi untuk dengan cepat menentukan apakah data telah diubah.

2.3 Contoh enkripsi

Contoh enkripsi dan dekripsi RSA menggunakan kode Java:

public class RsaDemo {    public static void main(String[] args) {        try {            KeyPair keyPair = generateRSAKeyPair();            PublicKey publicKey = keyPair.getPublic();            PrivateKey privateKey = keyPair.getPrivate();            String originalData = "Hello, 江南一点雨!";            String encryptedData = encrypt(publicKey, originalData);            String decryptedData = decrypt(privateKey, encryptedData);

            System.out.println("加密后的数据: " + encryptedData);
            System.out.println("解密后的数据: " + decryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }    public static String decrypt(PrivateKey privateKey, String encryptedData) throws Exception {        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));        return new String(decryptedBytes);
    }    public static String encrypt(PublicKey publicKey, String data) throws Exception {        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);        byte[] bytes = cipher.doFinal(data.getBytes());        return Base64.getEncoder().encodeToString(bytes);
    }    public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048); // 可以指定密钥长度,如2048位
        return keyGen.generateKeyPair();
    }
}

2.4 Memadukan frontend dan backend

Enkripsi di frontend, dekripsi di backend

Kode backend sama seperti contoh di atas. Bedanya, setelah mendapatkan kunci publik, kita dapat mencetaknya;kunci publik ini nantinya perlu dikirim ke antarmuka:

public static void main(String[] args) {    try {        KeyPair keyPair = generateRSAKeyPair();        PublicKey publicKey = keyPair.getPublic();        PrivateKey privateKey = keyPair.getPrivate();
        System.out.println("公钥: " + Base64.getEncoder().encodeToString(publicKey.getEncoded()));        String originalData = "Hello, 江南一点雨!";        String encryptedData = encrypt(publicKey, originalData);        String decryptedData = decrypt(privateKey, encryptedData);
        System.out.println("加密后的数据: " + encryptedData);
        System.out.println("解密后的数据: " + decryptedData);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Kode frontend adalah sebagai berikut:

<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RSA Encryption Example</title>
    <!-- 引入jsencrypt库 -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/bin/jsencrypt.min.js"></script></head><body><script>
    // 重写前端加密方法
    function encryptData(publicKey, data) {        // 创建一个新的JSEncrypt对象
        var encryptor = new JSEncrypt();        // 设置公钥
        encryptor.setPublicKey(publicKey);        // 加密数据
        var encrypted = encryptor.encrypt(data);        return encrypted;
    }    // 示例公钥(实际使用时应该替换为服务器提供的公钥)
    var publicKey = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnP5FOXIghidLDE2FgFwzi4Y+qTdYjnc0eMoiL5i4gdOeTE+7Uu9YvDB51GcBWKD9lvmZr5rX8z0OBpwe526qsTekNpXoIVk9DB34US0HOrAXEEwpUTVuSR656cJGAwmWBkVaalQynEz4Dlzrm53zExVYueruYUuzFyuZDaQcFnl3rWUH/XDkCIe23z0R1TfT3Q2OYNKft0u56r0S/ko99utXuYJK9yowe7QGT6q4cSJwsITQTomCARAq9q+bSNuGEYa4FlYCKIKWIhKbMhz0FYIMB2fJN10GyZbbvKASqeMkuCoD2Efgd8/6uMwOaMcx9LgEkcFaQ3qgDutsPXNUswIDAQAB`;    // 要加密的数据
    var data = 'Hello, javaboy!';    // 调用加密方法
    var encryptedData = encryptData(publicKey, data);    // 输出加密后的数据
    console.log('Encrypted Data:', encryptedData);</script></body></html>

Enkripsi di backend, dekripsi di frontend

Menggunakan algoritma RSA untuk mengenkripsi data di Java, lalu mendekripsinya di JavaScript, berarti sisi server mengenkripsi dengan kunci publik milik frontend, lalu frontend mendekripsi dengan kunci privatnya sendiri. Dalam skenario ini, kunci privat frontend sangat mudah dicuri, sehingga penggunaan seperti ini tidak disarankan. Karena itu, saya juga tidak akan memberikan contoh.

Baiklah, kita sudah kembali membahas enkripsi simetris dan asimetris bersama teman-teman. Semua kode contoh di atas sudah berhasil diuji oleh Kak Song, dan dapat teman-teman jadikan referensi.


Komentar

Mengirim

Pilih tema favorit

Tema pilihan berlaku di seluruh situs.