JavaRush /Java Blog /Random-TL /Seguridad sa Java: pinakamahusay na kagawian

Seguridad sa Java: pinakamahusay na kagawian

Nai-publish sa grupo
Sa mga application ng server, ang isa sa pinakamahalagang tagapagpahiwatig ay ang seguridad. Ito ay isa sa mga uri ng Non-Functional Requirements . Seguridad sa Java: pinakamahusay na kagawian - 1Ang seguridad ay may maraming bahagi. Siyempre, upang ganap na masakop ang lahat ng mga proteksiyon na prinsipyo at aksyon na alam, kailangan mong magsulat ng higit sa isang artikulo, kaya tumuon tayo sa pinakamahalaga. Ang isang taong bihasa sa paksang ito, ay magagawang i-set up ang lahat ng mga proseso at matiyak na hindi sila lumikha ng mga bagong butas sa seguridad, ay kinakailangan sa anumang koponan. Siyempre, hindi mo dapat isipin na kung susundin mo ang mga kasanayang ito, magiging ganap na ligtas ang application. Hindi! Ngunit tiyak na magiging mas ligtas sa kanila. Pumunta ka.

1. Magbigay ng seguridad sa antas ng wikang Java

Una sa lahat, ang seguridad sa Java ay nagsisimula mismo sa antas ng feature ng wika. Ito ang gagawin natin kung walang access modifiers?... Anarchy, no less. Tinutulungan kami ng isang programming language na magsulat ng secure na code at samantalahin din ang maraming implicit na feature ng seguridad:
  1. Malakas magtype. Ang Java ay isang statically typed na wika na nagbibigay ng kakayahang makakita ng mga error sa uri sa oras ng pagtakbo.
  2. I-access ang mga modifier. Salamat sa kanila, maaari naming i-configure ang access sa mga klase, pamamaraan at mga field ng klase sa paraang kailangan namin.
  3. Awtomatikong pamamahala ng memorya. Para sa layuning ito, kami (Javaists ;)) ay mayroong Garbage Collector, na nagpapalaya sa iyo mula sa manu-manong configuration. Oo, minsan may mga problema.
  4. Pagsusuri ng Bytecode: Nag-compile ang Java sa bytecode, na sinusuri ng runtime bago ito patakbuhin.
Sa iba pang mga bagay, may mga rekomendasyon mula sa Oracle sa seguridad. Siyempre, hindi ito nakasulat sa isang "mataas na istilo" at maaari kang makatulog nang maraming beses habang binabasa ito, ngunit sulit ito. Ang isang partikular na mahalagang dokumento ay ang Secure Coding Guidelines para sa Java SE , na nagbibigay ng mga tip sa kung paano magsulat ng secure na code. Ang dokumentong ito ay naglalaman ng maraming kapaki-pakinabang na impormasyon. Kung maaari, tiyak na sulit itong basahin. Upang mapukaw ang interes sa materyal na ito, narito ang ilang mga kagiliw-giliw na tip:
  1. Iwasang mag-serialize ng mga secure-sensitive na klase. Sa kasong ito, maaari mong makuha ang interface ng klase mula sa serialized na file, hindi banggitin ang data na ini-serialize.
  2. Subukang iwasan ang mga nababagong klase ng data. Ibinibigay nito ang lahat ng benepisyo ng mga hindi nababagong klase (hal. kaligtasan ng thread). Kung mayroong 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 nababago na bagay, maaaring baguhin ng code ng kliyente ang panloob na estado ng bagay.
  4. At iba pa…
Sa pangkalahatan, ang Secure Coding Guidelines para sa Java SE ay naglalaman ng isang hanay ng mga tip at trick kung paano magsulat ng code sa Java nang tama at secure.

2. Tanggalin ang kahinaan ng SQL injection

Natatanging kahinaan. Ang pagiging natatangi nito ay nakasalalay sa katotohanan na ito ay parehong isa sa pinakasikat at isa sa mga pinakakaraniwang kahinaan. Kung hindi ka interesado sa isyu ng seguridad, hindi mo malalaman ang tungkol dito. Ano ang SQL injection? Ito ay isang pag-atake sa isang database sa pamamagitan ng pag-iniksyon ng karagdagang SQL code kung saan hindi ito inaasahan. Sabihin nating mayroon kaming isang paraan na nangangailangan ng ilang parameter upang i-query ang database. Halimbawa, username. Ang code na may kahinaan ay magiging ganito ang hitsura:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Пишем sql request в базу данных с нашим firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;

   // выполняем request
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводит ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводит в коллекцию юзеров
}
Sa halimbawang ito, ang sql query ay inihanda nang maaga sa isang hiwalay na linya. Mukhang kung ano ang problema, tama ba? Siguro ang problema ay na ito ay mas mahusay na gamitin String.format? Hindi? Ano ngayon? Ilagay natin ang ating sarili sa lugar ng isang tester at isipin kung ano ang maaaring maihatid sa halaga firstName. Halimbawa:
  1. Maaari mong ipasa ang inaasahan - ang username. Pagkatapos ay ibabalik ng database ang lahat ng mga gumagamit na may ganoong pangalan.
  2. Maaari kang magpasa ng walang laman na string: pagkatapos ay ibabalik ang lahat ng user.
  3. O maaari mong ipasa ang sumusunod: “''; DROP TABLE USERS;”. At dito magkakaroon ng mas malalaking problema. Aalisin ng query na ito ang talahanayan mula sa database. Sa lahat ng data. LAHAT.
Naiisip mo ba kung anong mga problema ang maaaring idulot nito? Pagkatapos ay maaari mong isulat ang anumang gusto mo. Maaari mong baguhin ang pangalan ng lahat ng mga gumagamit, maaari mong tanggalin ang kanilang mga address. Malawak ang saklaw ng sabotahe. Upang maiwasan ito, kailangan mong ihinto ang pag-inject ng isang handa na query at sa halip ay buuin ito gamit ang mga parameter. Ito dapat ang tanging paraan upang mag-query sa database. Sa ganitong paraan maaari mong alisin ang kahinaan na ito. Halimbawa:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Создаем параметризированный request.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Создаем подготовленный стейтмент с параметризованным requestом
   PreparedStatement statement = connection.prepareStatement(query);

   // Передаем meaning параметра
   statement.setString(1, firstName);

   // выполняем request
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводим ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводим в коллекцию юзеров
}
Sa ganitong paraan, maiiwasan ang kahinaang ito. Para sa mga gustong sumabak nang mas malalim sa isyu kaysa sa artikulong ito, narito ang isang magandang halimbawa . Paano mo malalaman kung naiintindihan mo ang bahaging ito? Kung ang biro sa ibaba ay naging malinaw, kung gayon ito ay isang tiyak na senyales na ang kakanyahan ng kahinaan ay malinaw: D Seguridad sa Java: pinakamahusay na kagawian - 2

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

Ano ang ibig sabihin nito? Para sa mga hindi alam kung ano ang dependency, ipapaliwanag ko: ito ay isang jar archive na may code na nakakonekta 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. At kung pag-uusapan natin ang tungkol sa malalaking application, gumagamit sila ng maraming iba't ibang dependency. Ang ilan ay palipat (iyon ay, ang bawat dependency ay maaaring magkaroon ng sarili nitong mga dependency, at iba pa). Samakatuwid, ang mga umaatake ay lalong binibigyang pansin ang mga open-source na dependency, dahil ang mga ito ay regular na ginagamit at maaaring magdulot ng mga problema para sa maraming mga kliyente. Mahalagang tiyakin na walang kilalang mga kahinaan sa buong dependency tree (na kung ano mismo ang hitsura nito). At mayroong ilang mga paraan upang gawin ito.

Gamitin ang Snyk para sa pagsubaybay

Sinusuri ng tool ng Snyk ang lahat ng mga dependency ng proyekto at nagba-flag ng mga kilalang kahinaan. Doon maaari kang magparehistro at mag-import ng iyong mga proyekto sa pamamagitan ng GitHub, halimbawa. Seguridad sa Java: pinakamahusay na kagawian - 3Gayundin, tulad ng nakikita mo mula sa larawan sa itaas, kung ang isang mas bagong bersyon ay may solusyon sa kahinaang ito, mag-aalok ang Snyk na gawin ito at lumikha ng isang Pull-Request. Maaari itong magamit nang walang bayad para sa mga open-source na proyekto. Ang mga proyekto ay i-scan sa ilang dalas: 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: bukas na sila sa lahat). Susunod, 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 Pull-Request sa mga proyekto kung saan kailangang i-update ang mga dependency: Seguridad sa Java: pinakamahusay na kagawian - 5At narito ang isa pa: Seguridad sa Java: pinakamahusay na kagawian - 6Kaya ito ay isang mahusay na tool para sa paghahanap ng mga kahinaan at pagsubaybay para sa pag-update mga bagong bersyon.

Gamitin ang GitHub Security Lab

Magagamit din ng mga nagtatrabaho sa GitHub ang kanilang mga built-in na tool. Maaari kang magbasa nang higit pa tungkol sa diskarteng ito sa aking pagsasalin mula sa kanilang blog na Announcement of GitHub Security Lab . Ang tool na ito, siyempre, ay mas simple kaysa sa Snyk, ngunit tiyak na hindi mo ito dapat pabayaan. Dagdag pa, ang bilang ng mga kilalang kahinaan ay lalago lamang, kaya parehong lalawak at mapabuti ang Snyk at GitHub Security Lab.

I-activate ang Sonatype DepShield

Kung gumagamit ka ng GitHub upang iimbak ang iyong mga repositoryo, maaari kang magdagdag ng isa sa mga application sa iyong mga proyekto mula sa MarketPlace - Sonatype DepShield. Sa tulong nito, maaari mo ring i-scan ang mga proyekto para sa mga dependency. Bukod dito, kung may mahanap ito, isang GitHub Issue ang gagawin na may kaukulang paglalarawan, tulad ng ipinapakita sa ibaba: Seguridad sa Java: pinakamahusay na kagawian - 7

4. Pangasiwaan ang sensitibong data nang may pag-iingat

Seguridad sa Java: pinakamahusay na kagawian - 8Sa pagsasalita sa Ingles, ang pariralang "sensitive data" ay mas karaniwan. Ang pagsisiwalat ng personal na impormasyon, mga numero ng credit card at iba pang personal na impormasyon ng kliyente ay maaaring magdulot ng hindi na mapananauli na pinsala. Una sa lahat, kailangan mong tingnang mabuti ang disenyo ng application at matukoy kung ang anumang data ay talagang kailangan. Marahil ay hindi na kailangan ang ilan sa kanila, ngunit sila ay idinagdag para sa isang hinaharap na hindi pa dumarating at malamang na hindi darating. Bilang karagdagan, sa panahon ng pag-log ng proyekto, ang naturang data ay maaaring tumagas. Ang isang simpleng paraan upang pigilan ang sensitibong data na makapasok sa iyong mga log ay upang linisin ang mga pamamaraan toString()ng mga entity ng domain (gaya ng User, Student, Teacher, at iba pa). Pipigilan nito ang mga sensitibong field na hindi sinasadyang ma-print. Kung gagamit ka ng Lombok upang makabuo ng paraan toString(), maaari kang gumamit ng anotasyon @ToString.Excludeupang maiwasan ang paggamit ng field sa output sa pamamagitan ng pamamaraan toString(). Gayundin, maging maingat kapag nagbabahagi ng data sa labas ng mundo. Halimbawa, mayroong http endpoint na nagpapakita ng mga pangalan ng lahat ng user. Hindi na kailangang ipakita ang panloob na natatanging ID ng user. Bakit? Dahil sa paggamit nito, ang isang umaatake ay makakakuha ng iba, mas kumpidensyal na impormasyon tungkol sa bawat user. Halimbawa, kung gagamitin mo ang Jackson para i-serialize at i-deserialize ang mga POJO sa JSON , maaari mong gamitin ang @JsonIgnoreat mga anotasyon @JsonIgnorePropertiesupang maiwasang ma-serialize at deserialize ang mga partikular na field. Sa pangkalahatan, kailangan mong gumamit ng iba't ibang klase ng POJO para sa iba't ibang lugar. Ano ang ibig sabihin nito?
  1. Upang gumana sa database, gumamit lamang ng POJO - Entity.
  2. Upang gumana sa lohika ng negosyo, ilipat ang Entity sa Modelo.
  3. Upang makipagtulungan sa labas ng mundo at magpadala ng mga kahilingan sa http, gumamit ng mga ikatlong 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 customer ay dapat na ligtas na naka-imbak. Upang gawin ito kailangan mong gumamit ng pag-encrypt. Depende sa gawain, kailangan mong magpasya kung anong uri ng pag-encrypt ang gagamitin. Dagdag pa, 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 mong isulat ang algorithm sa iyong sarili. Ngunit ito ay hindi kailangan. Maaari mong samantalahin 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 paano ito gamitin, gamit ang halimbawa kung paano i-encrypt ang isang paraan at ang isa pa:
private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Цой жив!";
   String aad = "Юрий Клинских";

   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 password

Para sa gawaing ito, pinakaligtas na gumamit ng asymmetric encryption. Bakit? Dahil ang application ay talagang hindi kailangang i-decrypt ang mga password pabalik. Ito ang pangkalahatang diskarte. Sa totoo lang, kapag nagpasok ang isang user ng password, ine-encrypt ito ng system at ikinukumpara ito sa kung ano ang nasa vault ng password. Isinasagawa ang pag-encrypt gamit ang parehong paraan, kaya maaari mong asahan na magkatugma ang mga ito (kung ipinasok mo ang tamang password ;), siyempre). Ang BCrypt at SCrypt ay angkop para sa layuning ito. Parehong one-way na function (cryptographic hashes) na may computationally complex algorithm na tumatagal ng maraming oras. Ito ay eksakto kung ano ang kailangan mo, dahil ang pag-decipher nito nang direkta ay magtatagal magpakailanman. Halimbawa, sinusuportahan ng Spring Security ang isang hanay ng mga algorithm. SCryptPasswordEncoderMaaari mo ring gamitin ang BCryptPasswordEncoder. Ano ang isang malakas na algorithm ng pag-encrypt ngayon ay maaaring mahina sa susunod na taon. Bilang resulta, napagpasyahan namin na kinakailangang suriin ang mga algorithm na ginamit at i-update ang mga aklatan gamit ang mga algorithm.

Sa halip na output

Ngayon napag-usapan natin ang tungkol sa kaligtasan at, siyempre, maraming bagay ang naiwan sa mga eksena. Binuksan ko lang ang pinto sa isang bagong mundo para sa iyo: isang mundo na may sariling buhay. Sa seguridad ay kapareho ng sa pulitika: kung hindi ka nakikisali sa pulitika, ang pulitika ay sasali sa iyo. Ayon sa kaugalian, iminumungkahi kong mag-subscribe sa aking Github account . Doon ko ipino-post ang aking trabaho sa iba't ibang teknolohiya na aking pinag-aaralan at inilalapat sa trabaho.

kapaki-pakinabang na mga link

Oo, halos lahat ng mga artikulo sa site ay nakasulat sa Ingles. Gustuhin man natin o hindi, ang Ingles ay ang wika para sa mga programmer na makipag-usap. Ang lahat ng pinakabagong artikulo, aklat, at magasin sa programming ay nakasulat sa Ingles. Iyon ang dahilan kung bakit ang aking mga link sa mga rekomendasyon ay halos nasa English:
  1. Habr: SQL Injection para sa mga Nagsisimula
  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 pinakamahusay na kasanayan sa seguridad ng java
  7. JR: Anunsyo ng GitHub Security Lab: sabay na nagpoprotekta sa lahat ng iyong code
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION