CodeGym /Java Blog /Random /Seguridad sa Java: pinakamahusay na kagawian
John Squirrels
Antas
San Francisco

Seguridad sa Java: pinakamahusay na kagawian

Nai-publish sa grupo
Ang isa sa pinakamahalagang sukatan sa mga application ng server ay ang seguridad. Ito ay isang uri ng non-functional na pangangailangan . Seguridad sa Java: pinakamahusay na kagawian - 1Kasama sa seguridad ang maraming bahagi. Siyempre, kakailanganin ng higit sa isang artikulo upang ganap na masakop ang lahat ng kilalang mga prinsipyo sa seguridad at mga hakbang sa seguridad, kaya't tatalakayin natin ang pinakamahalaga. Ang isang taong bihasa sa paksang ito ay maaaring mag-set up ng lahat ng nauugnay na proseso, maiwasan ang paglikha ng mga bagong butas sa seguridad, at kakailanganin sa anumang koponan. Siyempre, hindi mo dapat isipin na magiging 100% secure ang iyong aplikasyon kung susundin mo ang mga kasanayang ito. Hindi! Ngunit tiyak na magiging mas secure ito sa kanila. Tara na.

1. Magbigay ng seguridad sa antas ng wikang Java

Una sa lahat, ang seguridad sa Java ay nagsisimula mismo sa antas ng mga kakayahan ng wika. Ano ang gagawin natin kung walang mga access modifier? Walang iba kundi anarkiya. Tinutulungan kami ng programming language na magsulat ng secure na code at gumagamit din ng maraming implicit na feature ng seguridad:
  1. Malakas magtype. Ang Java ay isang statically typed na wika. Ginagawa nitong posible na mahuli ang mga error na nauugnay sa uri sa runtime.
  2. I-access ang mga modifier. Nagbibigay-daan ito sa amin na i-customize ang access sa mga klase, pamamaraan, at field kung kinakailangan.
  3. Awtomatikong pamamahala ng memorya. Para dito, may tagakolekta ng basura ang mga developer ng Java na nagpapalaya sa amin mula sa manu-manong i-configure ang lahat. Oo, minsan may mga problema.
  4. Bytecode verification : Ang Java ay pinagsama-sama sa bytecode, na sinusuri ng runtime bago ito isagawa.
Bukod pa rito, may mga rekomendasyon sa seguridad ng Oracle . Siyempre, hindi ito nakasulat sa matayog na lenggwahe at baka makatulog ka ng ilang beses habang binabasa ito, pero sulit naman. Sa partikular, ang dokumentong pinamagatang Secure Coding Guidelines para sa Java SE ay mahalaga. Nagbibigay ito ng payo kung paano magsulat ng secure na code. Ang dokumentong ito ay naghahatid ng isang malaking halaga ng lubos na kapaki-pakinabang na impormasyon. Kung mayroon kang pagkakataon, dapat mong basahin ito. Upang mapukaw ang iyong interes sa materyal na ito, narito ang ilang mga kagiliw-giliw na tip:
  1. Iwasang mag-serialize ng mga klase na sensitibo sa seguridad. Inilalantad ng serialization ang interface ng klase sa serialized na file, hindi banggitin ang data na naka-serialize.
  2. Subukang iwasan ang mga nababagong klase para sa data. Nagbibigay ito ng lahat ng mga benepisyo ng mga hindi nababagong klase (hal. kaligtasan ng thread). Kung mayroon kang nababagong bagay, maaari itong humantong sa hindi inaasahang pag-uugali.
  3. Gumawa ng mga kopya ng ibinalik na nababagong mga bagay. Kung ang isang pamamaraan ay nagbabalik ng isang reference sa isang panloob na nababagong bagay, maaaring baguhin ng code ng kliyente ang panloob na estado ng bagay.
  4. At iba pa…
Karaniwan, ang Secure Coding Guidelines para sa Java SE ay isang koleksyon ng mga tip at trick kung paano isulat ang Java code nang tama at secure.

2. Tanggalin ang mga kahinaan ng SQL injection

Ito ay isang espesyal na uri ng kahinaan. Espesyal ito dahil pareho itong isa sa pinakasikat at isa sa mga pinakakaraniwang kahinaan. Kung hindi ka kailanman naging interesado sa seguridad ng computer, hindi mo malalaman ang tungkol dito. Ano ang SQL injection? Ito ay isang pag-atake sa database na nagsasangkot ng pag-iniksyon ng karagdagang SQL code kung saan hindi ito inaasahan. Ipagpalagay na mayroon kaming isang paraan na tumatanggap ng ilang uri ng parameter upang i-query ang database. Halimbawa, isang username. Ang vulnerable code ay magiging ganito:

// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);
  
   // Compose a SQL database query with our firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;
  
   // Execute the query
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
Sa halimbawang ito, ang isang SQL query ay inihanda nang maaga sa isang hiwalay na linya. Kaya ano ang problema, tama? Siguro ang problema ay mas mahusay na gumamit ng String.format ? Hindi? Well, ano kung gayon? Ilagay natin ang ating sarili sa posisyon ng isang tester at isipin kung ano ang maaaring ipasa bilang halaga ng firstName . Halimbawa:
  1. Mapapasa natin ang inaasahan — isang username. Pagkatapos ay ibabalik ng database ang lahat ng mga gumagamit na may ganoong pangalan.
  2. Maaari tayong magpasa ng walang laman na string. Pagkatapos ang lahat ng mga gumagamit ay ibabalik.
  3. Ngunit maaari rin nating ipasa ang mga sumusunod: "'; DROP TABLE USERS;". At narito kami ngayon ay may mga huuuuuuge problema. Ang query na ito ay magtatanggal ng isang talahanayan mula sa database. Kasama ang lahat ng data. LAHAT NG ITO.
Naiisip mo ba ang mga problemang idudulot nito? Higit pa riyan, maaari kang sumulat ng kahit anong gusto mo. Maaari mong baguhin ang mga pangalan ng lahat ng mga gumagamit. Maaari mong tanggalin ang kanilang mga address. Ang saklaw para sa sabotahe ay napakalawak. Upang maiwasan ito, kailangan mong pigilan ang pag-iniksyon ng isang handa na query at sa halip ay bumuo ng query gamit ang mga parameter. Ito dapat ang tanging paraan upang lumikha ng mga query sa database. Ito ay kung paano mo maaalis ang kahinaan na ito. Halimbawa:

// This method retrieves from the database all users with a certain name
public List findByFirstName(String firstName) throws SQLException {
   // Connect to the database
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Create a parameterized query.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Create a prepared statement with the parameterized query
   PreparedStatement statement = connection.prepareStatement(query);
  
   // Pass the parameter's value
   statement.setString(1, firstName);

   // Execute the query
   ResultSet result = statement.executeQuery(query);

   // Use mapToUsers to convert the ResultSet into a collection of users.
   return mapToUsers(result);
}

private List mapToUsers(ResultSet resultSet) {
   // Converts to a collection of users
}
Sa ganitong paraan maiiwasan ang kahinaan. Para sa mga gustong sumabak nang mas malalim sa artikulong ito, narito ang isang magandang halimbawa . Paano mo malalaman kung naiintindihan mo ang kahinaang ito? Kung nakuha mo ang biro sa komiks sa ibaba, malamang na mayroon kang malinaw na pagkaunawa sa kung ano ang tungkol sa kahinaan na ito :DSeguridad sa Java: pinakamahusay na kagawian - 2

3. I-scan ang mga dependency at panatilihing na-update ang mga ito

Anong ibig sabihin niyan? Kung hindi mo alam kung ano ang dependency, ipapaliwanag ko. Ang dependency ay isang JAR archive na may code na konektado sa isang proyekto gamit ang mga awtomatikong build system (Maven, Gradle, Ant) upang magamit muli ang solusyon ng ibang tao. Halimbawa, Project Lombok , na bumubuo ng mga getter, setter, atbp. para sa amin sa runtime. Ang mga malalaking application ay maaaring magkaroon ng marami at maraming dependencies. Ang ilan ay palipat (iyon ay, ang bawat dependency ay maaaring may sariling mga dependency, at iba pa). Bilang resulta, lalong binibigyang pansin ng mga umaatake ang mga open-source na dependency, dahil regular silang ginagamit at maraming kliyente ang maaaring magkaroon ng mga problema dahil sa kanila. Mahalagang tiyakin na walang kilalang mga kahinaan sa buong dependency tree (oo, mukhang puno ito). Mayroong ilang mga paraan upang gawin ito.

Gamitin ang Snyk para sa pagsubaybay sa dependency

Sinusuri ng Snyk ang lahat ng mga dependency ng proyekto at nagba-flag ng mga kilalang kahinaan. Maaari kang magparehistro sa Snyk at mag-import ng iyong mga proyekto sa pamamagitan ng GitHub. Seguridad sa Java: pinakamahusay na kagawian - 3Gayundin, tulad ng nakikita mo mula sa larawan sa itaas, kung ang isang kahinaan ay naayos sa isang mas bagong bersyon, pagkatapos ay iaalok ng Snyk ang pag-aayos at lumikha ng isang kahilingan sa paghila. Magagamit mo ito nang libre para sa mga open-source na proyekto. Ang mga proyekto ay ini-scan sa mga regular na pagitan, hal isang beses sa isang linggo, isang beses sa isang buwan. Nagrehistro ako at idinagdag ang lahat ng aking mga pampublikong repositoryo sa Snyk scan (walang mapanganib tungkol dito, dahil pampubliko na sila sa lahat). Pagkatapos ay ipinakita ni Snyk ang resulta ng pag-scan: Seguridad sa Java: pinakamahusay na kagawian - 4At pagkaraan ng ilang sandali, naghanda ang Snyk-bot ng ilang kahilingan sa pag-pull sa mga proyekto kung saan kailangang i-update ang mga dependency: Seguridad sa Java: pinakamahusay na kagawian - 5At gayundin:Seguridad sa Java: pinakamahusay na kagawian - 6Ito ay isang mahusay na tool para sa paghahanap ng mga kahinaan at pagsubaybay sa mga update para sa mga bagong bersyon.

Gamitin ang GitHub Security Lab

Maaaring samantalahin ng sinumang nagtatrabaho sa GitHub ang mga built-in na tool nito. Maaari kang magbasa nang higit pa tungkol sa diskarteng ito sa kanilang post sa blog na pinamagatang Announcing GitHub Security Lab . Ang tool na ito, siyempre, ay mas simple kaysa sa Snyk, ngunit tiyak na hindi mo ito dapat pabayaan. Higit pa, ang bilang ng mga kilalang kahinaan ay lalago lamang, kaya parehong Snyk at GitHub Security Lab ay patuloy na lalawak at mapabuti.

Paganahin ang Sonatype DepShield

Kung gumagamit ka ng GitHub upang iimbak ang iyong mga repositoryo, maaari mong idagdag ang Sonatype DepShield, isa sa mga application sa MarketPlace, sa iyong mga proyekto. Maaari rin itong magamit upang i-scan ang mga proyekto para sa mga dependency. Bukod dito, kung may mahanap ito, bubuo ang isang Isyu sa GitHub na may naaangkop na paglalarawan tulad ng ipinapakita sa ibaba:Seguridad sa Java: pinakamahusay na kagawian - 7

4. Pangasiwaan ang kumpidensyal na data nang may pag-iingat

Maaari naming alternatibong gamitin ang pariralang "sensitive data." Ang pag-leak ng personal na impormasyon ng customer, mga numero ng credit card, at iba pang sensitibong impormasyon ay maaaring magdulot ng hindi na mapananauli na pinsala. Una sa lahat, tingnang mabuti ang disenyo ng iyong aplikasyon at tukuyin kung kailangan mo talaga ito o ang data na iyon. Marahil ay hindi mo talaga kailangan ang ilan sa data na mayroon ka — data na idinagdag para sa isang hinaharap na hindi dumating at malamang na hindi darating. Bukod pa rito, marami kang hindi sinasadyang tumagas ng naturang data sa pamamagitan ng pag-log. Ang isang madaling paraan upang pigilan ang sensitibong data mula sa pagpasok ng iyong mga log ay ang pag-scrub ng toString() na mga paraan ng mga entity ng domain (gaya ng User, Student, Teacher, atbp.). Pipigilan ka nito mula sa aksidenteng paglabas ng mga kumpidensyal na field. Kung gagamit ka ng Lombok para makabuo ng toString()paraan, maaari mong gamitin ang @ToString.Exclude annotation upang pigilan ang isang field na magamit sa output ng toString() method. Gayundin, maging maingat kapag nagpapadala ng data sa labas ng mundo. Ipagpalagay na mayroon kaming HTTP endpoint na nagpapakita ng mga pangalan ng lahat ng user. Hindi na kailangang magpakita ng natatanging internal ID ng user. Bakit? Dahil maaaring gamitin ito ng isang umaatake upang makakuha ng iba, mas sensitibong impormasyon tungkol sa user. Halimbawa, kung gagamitin mo si Jackson para i-serialize/deserialize ang isang POJO papunta/mula sa JSON , maaari mong gamitin ang @JsonIgnore at @JsonIgnorePropertiesmga anotasyon upang maiwasan ang serialization/deserialization ng mga partikular na field. Sa pangkalahatan, kailangan mong gumamit ng iba't ibang klase ng POJO sa iba't ibang lugar. Anong ibig sabihin niyan?
  1. Kapag nagtatrabaho sa isang database, gumamit ng isang uri ng POJO (isang entity).
  2. Kapag nagtatrabaho sa lohika ng negosyo, i-convert ang isang entity sa isang modelo.
  3. Kapag nagtatrabaho sa labas ng mundo at nagpapadala ng mga kahilingan sa HTTP, gumamit ng iba't ibang entity (DTO).
Sa ganitong paraan malinaw mong matutukoy kung aling mga field ang makikita mula sa labas at alin ang hindi.

Gumamit ng malakas na encryption at hashing algorithm

Ang kumpidensyal na data ng mga customer ay dapat na ligtas na naka-imbak. Para magawa ito, kailangan nating gumamit ng encryption. Depende sa gawain, kailangan mong magpasya kung aling uri ng pag-encrypt ang gagamitin. Bilang karagdagan, ang mas malakas na pag-encrypt ay tumatagal ng mas maraming oras, kaya muli kailangan mong isaalang-alang kung gaano karaming ang pangangailangan para dito ay nagbibigay-katwiran sa oras na ginugol dito. Siyempre, maaari kang magsulat ng isang algorithm ng pag-encrypt sa iyong sarili. Ngunit ito ay hindi kailangan. Maaari mong gamitin ang mga kasalukuyang solusyon sa lugar na ito. Halimbawa, ang Google Tink :

<!-- https://mvnrepository.com/artifact/com.google.crypto.tink/tink -->
<dependency>
   <groupid>com.google.crypto.tink</groupid>
   <artifactid>tink</artifactid>
   <version>1.3.0</version>
</dependency>
Tingnan natin kung ano ang gagawin, gamit ang halimbawang ito na kinasasangkutan ng pag-encrypt at pag-decryption:

private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Elvis lives!";
   String aad = "Buddy Holly";

   Aead aead = handle.getPrimitive(Aead.class);
   byte[] encrypted = aead.encrypt(plaintext.getBytes(), aad.getBytes());
   String encryptedString = Base64.getEncoder().encodeToString(encrypted);
   System.out.println(encryptedString);

   byte[] decrypted = aead.decrypt(Base64.getDecoder().decode(encrypted), aad.getBytes());
   System.out.println(new String(decrypted));
}

Pag-encrypt ng mga password

Para sa gawaing ito, pinakaligtas na gumamit ng asymmetric encryption. Bakit? Dahil hindi talaga kailangang i-decrypt ng application ang mga password. Ito ang karaniwang diskarte. Sa katotohanan, kapag ang isang user ay nagpasok ng isang password, ini-encrypt ito ng system at inihahambing ito sa kung ano ang umiiral sa tindahan ng password. Ang parehong proseso ng pag-encrypt ay ginagawa, kaya maaari naming asahan na sila ay magkatugma, kung ang tamang password ay ipinasok, siyempre :) Ang BCrypt at SCrypt ay angkop dito. Parehong one-way na function (cryptographic hashes) na may computationally complex algorithm na tumatagal ng mahabang panahon. Ito ay eksakto kung ano ang kailangan namin, dahil ang direktang pag-compute ay magtatagal magpakailanman (mabuti, mahaba, mahabang panahon). Sinusuportahan ng Spring Security ang isang buong hanay ng mga algorithm. Magagamit namin ang SCryptPasswordEncoder at BCryptPasswordEncoder. Ang kasalukuyang itinuturing na isang malakas na algorithm ng pag-encrypt ay maaaring ituring na mahina sa susunod na taon. Bilang resulta, ipinapalagay namin na dapat naming regular na suriin ang mga algorithm na ginagamit namin at, kung kinakailangan, i-update ang mga library na naglalaman ng mga algorithm ng pag-encrypt.

Sa halip na isang konklusyon

Ngayon ay pinag-usapan namin ang tungkol sa seguridad at, natural, maraming bagay ang naiwan sa mga eksena. Binuksan ko na lang ang pinto sa isang bagong mundo para sa iyo, isang mundo na may sariling buhay. Ang seguridad ay parang pulitika: kung hindi mo abalahin ang iyong sarili sa pulitika, abala ang pulitika sa iyo. Karaniwan kong iminumungkahi na sundan mo ako sa GitHub account . Doon ko ipino-post ang aking mga likha na may kinalaman sa iba't ibang teknolohiya na aking pinag-aaralan at inilalapat sa trabaho.

Mga kapaki-pakinabang na link

  1. Guru99: Tutorial sa SQL Injection
  2. Oracle: Java Security Resource Center
  3. Oracle: Secure Coding Guidelines para sa Java SE
  4. Baeldung: Ang Mga Pangunahing Kaalaman ng Java Security
  5. Katamtaman: 10 tip para palakasin ang iyong seguridad sa Java
  6. Snyk: 10 pinakamahuhusay na kagawian sa seguridad ng Java
  7. GitHub: Inanunsyo ang GitHub Security Lab: pag-secure ng code ng mundo, nang magkasama
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION