CodeGym/Java Blog/무작위의/Java의 보안: 모범 사례
John Squirrels
레벨 41
San Francisco

Java의 보안: 모범 사례

무작위의 그룹에 게시되었습니다
회원
서버 애플리케이션에서 가장 중요한 메트릭 중 하나는 보안입니다. 이것은 비기능적 요구사항 의 한 유형입니다 . Java의 보안: 모범 사례 - 1보안에는 많은 구성 요소가 포함됩니다. 물론 알려진 모든 보안 원칙과 보안 조치를 완전히 다루려면 두 개 이상의 문서가 필요하므로 가장 중요한 항목에 대해 살펴보겠습니다. 이 주제에 정통한 사람은 모든 관련 프로세스를 설정하고 새로운 보안 허점 생성을 방지할 수 있으며 모든 팀에 필요합니다. 물론 이러한 방법을 따르면 응용 프로그램이 100% 안전할 것이라고 생각해서는 안 됩니다. 아니요! 그러나 그들과 함께라면 확실히 더 안전할 것입니다. 갑시다.

1. Java 언어 수준의 보안 제공

우선 Java의 보안은 언어 기능 수준에서 바로 시작됩니다. 액세스 수정자가 없으면 어떻게 해야 합니까? 무정부 상태 외에는 아무것도 없을 것입니다. 프로그래밍 언어는 보안 코드를 작성하는 데 도움이 되며 많은 암시적 보안 기능을 사용합니다.
  1. 강력한 타이핑. Java는 정적으로 유형이 지정된 언어입니다. 이렇게 하면 런타임에 유형 관련 오류를 포착할 수 있습니다.
  2. 액세스 한정자. 이를 통해 필요에 따라 클래스, 메서드 및 필드에 대한 액세스를 사용자 지정할 수 있습니다.
  3. 자동 메모리 관리. 이를 위해 Java 개발자는 모든 것을 수동으로 구성하지 않아도 되는 가비지 수집기를 사용합니다. 예, 때때로 문제가 발생합니다.
  4. 바이트코드 확인 : Java는 바이트코드로 컴파일되며 실행되기 전에 런타임에 의해 확인됩니다.
또한 Oracle의 보안 권장 사항이 있습니다 . 물론 고상한 언어로 쓰여진 것이 아니며 읽는 동안 몇 번이나 잠들 수 있지만 그만한 가치가 있습니다. 특히 Secure Coding Guidelines for Java SE 라는 제목의 문서가 중요합니다. 보안 코드를 작성하는 방법에 대한 조언을 제공합니다. 이 문서는 많은 양의 매우 유용한 정보를 전달합니다. 기회가 되신다면 꼭 읽어보시길 바랍니다. 이 자료에 대한 관심을 불러일으키기 위해 다음은 몇 가지 흥미로운 팁입니다.
  1. 보안에 민감한 클래스를 직렬화하지 마십시오. 직렬화는 직렬화된 데이터는 말할 것도 없고 직렬화된 파일의 클래스 인터페이스를 노출합니다.
  2. 데이터에 대한 변경 가능한 클래스를 피하십시오. 이는 불변 클래스의 모든 이점(예: 스레드 안전성)을 제공합니다. 변경 가능한 개체가 있는 경우 예기치 않은 동작이 발생할 수 있습니다.
  3. 반환된 가변 객체의 복사본을 만듭니다. 메서드가 내부 가변 객체에 대한 참조를 반환하면 클라이언트 코드가 객체의 내부 상태를 변경할 수 있습니다.
  4. 등등…
기본적으로 Java SE용 보안 코딩 지침은 Java 코드를 정확하고 안전하게 작성하는 방법에 대한 팁과 요령 모음입니다.

2. SQL 인젝션 취약점 제거

이것은 특별한 종류의 취약점입니다. 가장 유명하고 가장 일반적인 취약점 중 하나이기 때문에 특별합니다. 컴퓨터 보안에 관심이 없다면 알지 못할 것입니다. SQL 인젝션이란? 예상하지 못한 곳에 추가 SQL 코드를 주입하는 데이터베이스 공격입니다. 데이터베이스를 쿼리하기 위해 일종의 매개 변수를 허용하는 메서드가 있다고 가정합니다. 예를 들어 사용자 이름입니다. 취약한 코드는 다음과 같습니다.
// 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
}
이 예에서 SQL 쿼리는 별도의 라인에 미리 준비됩니다. 그래서 뭐가 문제죠? 아마도 문제는 String.format 을 사용하는 것이 더 나을 것이라는 것입니까 ? 아니요? 글쎄, 그럼? 테스터의 입장이 되어 firstName 의 값으로 전달될 수 있는 것이 무엇인지 생각해 봅시다 . 예를 들어:
  1. 예상되는 것, 즉 사용자 이름을 전달할 수 있습니다. 그런 다음 데이터베이스는 해당 이름을 가진 모든 사용자를 반환합니다.
  2. 빈 문자열을 전달할 수 있습니다. 그런 다음 모든 사용자가 반환됩니다.
  3. 그러나 "'; DROP TABLE USERS;"를 전달할 수도 있습니다. 그리고 여기서 우리는 이제 huuuuuuge 문제가 있습니다. 이 쿼리는 데이터베이스에서 테이블을 삭제합니다. 모든 데이터와 함께. 그것의 모든.
이것이 야기할 문제를 상상할 수 있습니까? 그 외에도 원하는대로 쓸 수 있습니다. 모든 사용자의 이름을 변경할 수 있습니다. 주소를 삭제할 수 있습니다. 방해 행위의 범위는 엄청납니다. 이를 방지하기 위해서는 기성 쿼리의 주입을 방지하고 대신 매개 변수를 사용하여 쿼리를 구성해야 합니다. 이것이 데이터베이스 쿼리를 생성하는 유일한 방법이어야 합니다. 이것이 이 취약점을 제거하는 방법입니다. 예를 들어:
// 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
}
이렇게 하면 취약점을 피할 수 있습니다. 이 기사에 대해 더 자세히 알아보고 싶은 분들을 위해 여기에 좋은 예가 있습니다 . 이 취약점을 언제 이해하는지 어떻게 알 수 있습니까? 아래 만화에서 농담을 들었다면 이 취약점이 무엇인지 명확하게 이해하셨을 것입니다 :DJava의 보안: 모범 사례 - 2

3. 종속성을 스캔하고 업데이트 유지

그게 무슨 뜻이야? 종속성이 무엇인지 모르는 경우 설명하겠습니다. 종속성은 다른 사람의 솔루션을 재사용하기 위해 자동 빌드 시스템(Maven, Gradle, Ant)을 사용하여 프로젝트에 연결된 코드가 포함된 JAR 아카이브입니다. 예를 들어 런타임에 getter, setter 등을 생성하는 Project Lombok이 있습니다. 대규모 응용 프로그램은 매우 많은 종속성을 가질 수 있습니다. 일부는 전이적입니다(즉, 각 종속성이 고유한 종속성을 가질 수 있음). 그 결과 공격자들은 오픈 소스 종속성이 정기적으로 사용되고 많은 클라이언트가 이로 인해 문제를 겪을 수 있기 때문에 점점 더 관심을 기울이고 있습니다. 전체 종속성 트리에 알려진 취약점이 없는지 확인하는 것이 중요합니다(예, 트리처럼 보입니다). 이를 수행하는 방법에는 여러 가지가 있습니다.

종속성 모니터링에 Snyk 사용

Snyk는 모든 프로젝트 종속성을 확인하고 알려진 취약점에 플래그를 지정합니다. Snyk에 등록하고 GitHub를 통해 프로젝트를 가져올 수 있습니다. Java의 보안: 모범 사례 - 3또한 위의 그림에서 볼 수 있듯이 최신 버전에서 취약점이 수정되면 Snyk가 수정 사항을 제공하고 풀 요청을 생성합니다. 오픈 소스 프로젝트에 무료로 사용할 수 있습니다. 프로젝트는 정기적인 간격(예: 일주일에 한 번, 한 달에 한 번)으로 스캔됩니다. 모든 공개 리포지토리를 Snyk 스캔에 등록하고 추가했습니다(이미 모든 사람에게 공개되어 있기 때문에 이에 대해 위험한 것은 없습니다). 그런 다음 Snyk는 스캔 결과를 보여주었습니다. Java의 보안: 모범 사례 - 4잠시 후 Snyk-bot은 종속성을 업데이트해야 하는 프로젝트에서 몇 가지 풀 요청을 준비했습니다. Java의 보안: 모범 사례 - 5또한:Java의 보안: 모범 사례 - 6이는 취약점을 찾고 새 버전에 대한 업데이트를 모니터링하는 훌륭한 도구입니다.

GitHub 보안 랩 사용

GitHub에서 작업하는 사람은 누구나 내장 도구를 활용할 수 있습니다. Announcing GitHub Security Lab 이라는 제목의 블로그 게시물에서 이 접근 방식에 대해 자세히 알아볼 수 있습니다 . 물론 이 도구는 Snyk보다 간단하지만 무시해서는 안 됩니다. 또한 알려진 취약점의 수는 계속 증가할 것이므로 Snyk와 GitHub Security Lab은 계속해서 확장되고 개선될 것입니다.

Sonatype DepShield 활성화

GitHub를 사용하여 리포지토리를 저장하는 경우 MarketPlace의 애플리케이션 중 하나인 Sonatype DepShield를 프로젝트에 추가할 수 있습니다. 프로젝트의 종속성을 스캔하는 데에도 사용할 수 있습니다. 또한 무언가를 찾으면 아래와 같이 적절한 설명과 함께 GitHub 문제가 생성됩니다.Java의 보안: 모범 사례 - 7

4. 기밀 데이터를 주의해서 다루십시오.

또는 "민감한 데이터"라는 문구를 사용할 수도 있습니다. 고객의 개인 정보, 신용 카드 번호 및 기타 민감한 정보가 유출되면 돌이킬 수 없는 피해를 입을 수 있습니다. 우선, 애플리케이션의 디자인을 면밀히 살펴보고 이 데이터 또는 저 데이터가 정말로 필요한지 결정하십시오. 어쩌면 당신이 가지고 있는 데이터 중 일부는 실제로 필요하지 않을 수도 있습니다. 오지 않았고 오지 않을 것 같은 미래를 위해 추가된 데이터입니다. 또한 로깅을 통해 실수로 이러한 데이터를 유출하는 경우가 많습니다. 민감한 데이터가 로그에 입력되지 않도록 하는 쉬운 방법은 도메인 엔터티(예: User, Student, Teacher 등)의 toString() 메서드를 스크럽하는 것입니다. 이렇게 하면 실수로 기밀 필드를 출력하는 것을 방지할 수 있습니다. Lombok을 사용하여 toString()을 생성하는 경우메서드에서 @ToString.Exclude 주석을 사용하여 toString() 메서드 의 출력에서 ​​필드가 사용되지 않도록 할 수 있습니다 . 또한 외부 세계로 데이터를 보낼 때 매우 주의하십시오. 모든 사용자의 이름을 표시하는 HTTP 엔드포인트가 있다고 가정합니다. 사용자 고유의 내부 ID를 표시할 필요가 없습니다. 왜? 공격자가 이를 사용하여 사용자에 대한 더 민감한 다른 정보를 얻을 수 있기 때문입니다. 예를 들어, JSON 에서 POJO를 직렬화/역직렬화하기 위해 Jackson을 사용하는 경우 @JsonIgnore@JsonIgnoreProperties를 사용할 수 있습니다.특정 필드의 직렬화/역직렬화를 방지하기 위한 주석. 일반적으로 서로 다른 위치에서 서로 다른 POJO 클래스를 사용해야 합니다. 그게 무슨 뜻이야?
  1. 데이터베이스로 작업할 때 한 가지 유형의 POJO(엔티티)를 사용합니다.
  2. 비즈니스 로직으로 작업할 때 엔터티를 모델로 변환합니다.
  3. 외부 세계와 작업하고 HTTP 요청을 보낼 때 다른 엔터티(DTO)를 사용합니다.
이렇게 하면 외부에서 볼 수 있는 필드와 그렇지 않은 필드를 명확하게 정의할 수 있습니다.

강력한 암호화 및 해싱 알고리즘 사용

고객의 기밀 데이터는 안전하게 저장되어야 합니다. 이를 위해서는 암호화를 사용해야 합니다. 작업에 따라 사용할 암호화 유형을 결정해야 합니다. 또한 더 강력한 암호화는 더 많은 시간이 걸리므로 암호화에 대한 필요성이 얼마나 많은 시간을 소비하는 것을 정당화하는지 다시 한 번 고려해야 합니다. 물론 암호화 알고리즘을 직접 작성할 수 있습니다. 그러나 이것은 불필요합니다. 이 영역에서 기존 솔루션을 사용할 수 있습니다. 예를 들어 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>
암호화 및 암호 해독과 관련된 다음 예제를 사용하여 수행할 작업을 살펴보겠습니다.
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));
}

암호 암호화

이 작업에서는 비대칭 암호화를 사용하는 것이 가장 안전합니다. 왜? 응용 프로그램이 실제로 암호를 해독할 필요가 없기 때문입니다. 이것이 표준 접근 방식입니다. 실제로 사용자가 암호를 입력하면 시스템이 암호를 암호화하고 암호 저장소에 있는 것과 비교합니다. 동일한 암호화 과정이 진행되기 때문에 올바른 비밀번호가 입력된다면 당연히 일치할 것으로 예상할 수 있습니다 :) BCrypt와 SCrypt가 여기에 적합합니다. 둘 다 오랜 시간이 걸리는 계산적으로 복잡한 알고리즘을 사용하는 단방향 함수(암호화 해시)입니다. 이것이 바로 우리가 필요로 하는 것입니다. 왜냐하면 직접 계산은 영원히 걸릴 것이기 때문입니다. Spring Security는 모든 범위의 알고리즘을 지원합니다. SCryptPasswordEncoderBCryptPasswordEncoder를 사용할 수 있습니다.. 현재 강력한 암호화 알고리즘으로 간주되는 것이 내년에는 약한 것으로 간주될 수 있습니다. 그 결과 우리가 사용하는 알고리즘을 정기적으로 확인하고 필요에 따라 암호화 알고리즘이 포함된 라이브러리를 업데이트해야 한다는 결론을 내렸습니다.

결론 대신

오늘 우리는 보안에 대해 이야기했고 자연스럽게 많은 것들이 보이지 않게 남겨졌습니다. 나는 방금 당신을 위해 새로운 세계, 그 자체의 삶이 있는 세계의 문을 열었습니다. 보안은 정치와 같습니다. 정치에 바쁘지 않으면 정치가 당신에게 바쁠 것입니다. 나는 전통적으로 GitHub 계정 에서 나를 팔로우할 것을 제안합니다 . 거기에 제가 공부하고 직장에서 적용하고 있는 다양한 기술과 관련된 창작물을 게시합니다.

유용한 링크

  1. Guru99: SQL 인젝션 튜토리얼
  2. Oracle: 자바 보안 리소스 센터
  3. Oracle: Java SE용 보안 코딩 지침
  4. Baeldung: 자바 보안의 기초
  5. Medium: Java 보안 강화를 위한 10가지 팁
  6. Snyk: 10가지 Java 보안 모범 사례
  7. GitHub: GitHub Security Lab 발표: 전 세계의 코드를 함께 보호
코멘트
  • 인기
  • 신규
  • 이전
코멘트를 남기려면 로그인 해야 합니다
이 페이지에는 아직 코멘트가 없습니다