12306 제3자 소프트웨어 검 측 에 관 한 연구

11272 단어
전재 출처 를 밝 혀 주 십시오:http://blog.csdn.net/tang9140/article/details/42869269
먼저 본 문장 은 순 전 히 개인 이 좋아 하 는 기술적 연구 로 서 불법 조작 으로 부당 한 이익 을 얻 는 데 사용 하지 마 세 요. 알 잖 아 요.
문 제 를 야기 하 다.
2014 년 12 월 17, 18 일 에 12306 의 티켓 팅 소프트웨어 에 대해 불법 요청 이나 '제3자 티켓 팅 소프트웨어 사용' 알림 이 대량으로 발생 했 고 인증 코드 인식 에 오류 가 발생 했다.이후 12306 은 악성 티켓 강탈 소프트웨어, 플러그 인 에 대한 억제 와 통 제 를 위해 수단 을 통 해 티켓 강탈 소프트웨어 와 사용자 가 표를 구 매 하 는 행위 의 차 이 를 식별 할 수 있다 는 것 을 알 게 되 었 다.
그럼 문제 가 생 겼 습 니 다.나 는 그들 이 어떤 수단 을 써 서 했 는 지 궁금 했다.
보충 적 으로 저 는 예전 에 JAVA 버 전의 티켓 강탈 소프트웨어 를 한 적 이 있 습 니 다. 학습 용도 만 하고 다른 목적 은 없습니다.12306 에 제3자 소프트웨어 검 사 를 추가 한 후에 나의 개표 소프트웨어 역시 '불법 요청' 알림 이 나 와 로그 인 조차 들 어 갈 수 없 었 다.그래서 나의 사 고 를 불 러 일 으 켰 다.
티켓 강탈 소프트웨어 의 본질은 무엇 입 니까?
저 는 개인 적 으로 티켓 강탈 소프트웨어 는 로봇 (실제로 로봇 은 하나의 소프트웨어 이 고 여 기 는 이미지 표현 일 뿐) 으로 실물 을 대신 해서 차 표를 구 매 하 는 것 이 라 고 생각 합 니 다.모두 가 기계 의 반응 이 빠르다 는 것 을 알 고 있 기 때문에 동시에 빼 앗 는 전제 에서 기계 가 실물 보다 빠 를 것 이다. 이것 도 황소 당 이 차 표를 살 수 있 는 이유 이 고 네가 차 표를 살 수 없 는 이유 이다.로봇 이 왜 실물 인 을 대신 해서 표를 사 는 일련의 조작 을 할 수 있 는 지 계속 생각해 보 자.이것 은 바로 다음 문 제 를 야기 시 켰 다.
사용자 가 브 라 우 저 에서 차 표를 구 매 하 는 일련의 조작 은 기술적 인 측면 에서 볼 때 본질 적 으로 무슨 일이 발생 했 습 니까?
알다 시 피 12306 사 이 트 는 B / S 구 조 를 바탕 으로 하 는 WEB 서 비 스 를 제공 하고 http 프로 토 콜 위 에 세 워 진 서비스 입 니 다.http 프로 토 콜 은 전형 적 인 요청 - 응답 모드 의 프로 토 콜 로 상태 가 없 는 프로 토 콜 입 니 다.실제 사용자 가 브 라 우 저 에서 하 는 모든 작업 은 최종 적 으로 브 라 우 저 를 바탕 으로 서버 에 요청 을 보 내 고 서버 가 요청 을 받 은 후에 해당 하 는 업무 처 리 를 하고 응답 결 과 를 브 라 우 저 에 되 돌려 주 며 브 라 우 저 는 사용자 에 게 다시 표시 합 니 다.더 구체 적 으로 브 라 우 저 는 사용자 의 각종 이벤트 (예 를 들 어 마우스 클릭 이벤트, 키보드 입력 이벤트) 를 받 은 후 배경 에서 http 요청 을 서버 에 보 내 고 브 라 우 저 배경 은 서버 의 응답 내용 을 받 아 HTML 페이지 로 보 여 줍 니 다.
더욱 일반화 되 어 서버 로 서 는 HTTP 프로 토 콜 에 부합 되 는 요청 이 들 어 오 면 처 리 됩 니 다. 요청 이 브 라 우 저 를 통 해 보 내 는 지, 티켓 팅 소프트웨어 를 통 해 보 내 는 지 에 관심 이 없습니다 (브 라 우 저 자체 도 소프트웨어 이 고 일반 운영 체제 에 서 는 브 라 우 저 를 가지 고 있 습 니 다).위의 분석 을 통 해 알 수 있 듯 이 제3자 소프트웨어 가 브 라 우 저 를 완전히 모 의 하여 규범 에 맞 는 HTTP 요청 을 보 내 면 WEB 서버 는 합 법 적 인 요청 으로 처리 할 것 이다.그러면 문제 가 또 생 겼 습 니 다. 왜 개표 소프트웨어 에 '불법 요청' 알림 이 나 왔 습 니까? 이것 은 당신 이 방금 분석 한 것 과 모순 되 지 않 습 니까?글 쎄, 사실은 모순 되 지 않 아.제 가 방금 말 한 것 은 '완전 시 뮬 레이 션 브 라 우 저' 입 니 다. 예전 에 학습 판 으로 표 뺏 기 소프트웨어 가 브 라 우 저 방식 으로 HTTP 요청 을 엄 격 히 보 내지 않 았 기 때문에 12306 은 일부 기술 수단 을 통 해 불법 요 구 를 검출 할 수 있 습 니 다.그렇다면 우리 의 궁극 적 인 문 제 를 불 러 일 으 켰 다.
12306 기술 검 사 를 어떻게 하 는 지 정상 적 인 요구 와 불법 요 구 를 어떻게 구별 합 니까?
이 질문 에 대답 하기 전에 여러분 은 먼저 HTTP 프로 토 콜 을 알 아야 합 니 다.HTTP 요청 메 시 지 는 네 부분 으로 나 뉜 다. 요청 줄, 요청 헤더, 빈 줄, 선택 가능 한 요청 메시지 체;모두 8 가지 요청 방법 이 있 는데 가장 많이 사용 되 는 것 은 두 가지 입 니 다. GET 요청 과 POST 요청 입 니 다.GET 는 URL 뒤에 파 라 메 터 를 가 져 다 달라 고 요 청 했 고, POST 는 메시지 에 파 라 메 터 를 가 져 다 달라 고 요청 했다.요청 헤더 에 쿠키 정보 등 이 포 함 될 수 있 습 니 다.본론 으로 돌아 가면 12306 은 불법 검 측 요청 에 대해 세 가지 측면, 즉 요청 헤드, Cookie, 요청 매개 변 수 를 검사 하지 않 을 수 없다.
12306 검 측 세 가지 측면:
1. 요청 헤더
HTTP 요청 을 모 의 할 때 요청 헤더 의 순 서 를 주의해 야 합 니 다.본인 테스트 를 거 쳐 로그 인 요청 '쿠키 요청 헤더' 가 '커 넥 션 요청 헤더' 뒤에 놓 이면 '불법 요청' 을 안내 합 니 다.
2、Cookie
쿠키 요청 헤더 역시 순 서 를 주의해 야 합 니 다. 먼저 JSESSIONID (중간 다른 쿠키) 이 고 마지막 으로 BIGipServerotn, current 입 니 다.captcha_type
3. 요청 매개 변수
순서 적 인 문제 인지 브 라 우 저 에서 요청 파 라 메 터 를 보 내 는 순 서 를 엄 격 히 참조 하여 보 내 십시오.이외에 도 12306 은 주문 서 를 로그 인하 고 제출 할 때 동적 키 검증 을 추가 했다.동적 js 파일 의 url 주 소 를 가 져 온 다음 에 이 js 파일 내용 을 방문 하여 key 값 을 추출 하고 이 js 파일 의 암호 화 알고리즘 으로 key 암호 화 후 해당 하 는 value 값 을 만들어 야 합 니 다.
상술 한 세 가지 검 사 를 통 해 일부 불법 요 구 를 발견 할 수 있다.물론 제3자 소프트웨어 검 측 도 있 고 반 검 측 도 있다.12306 에서 이 일련의 제3자 소프트웨어 검 측, 감시 와 검증 코드 교체 (나 는 세 개의 서로 다른 검증 코드 가 있다 고 믿는다) 를 출시 한 후 몇 시간 동안 일부 개표 소프트웨어 가 해결 되 었 다.사실 저 는 이른바 제3자 소프트웨어 검 측 은 완전히 근본 적 인 문 제 를 해결 하지 않 는 것 이 라 고 생각 합 니 다. 마치 게임 중의 반 외 장 검 측 처럼 강력 한 외 장 연구 개발 자 들 이 보기에 일 격 에 견 디 지 못 합 니 다.네가 몇 번 이나 검증 을 했 든 지 간 에 아무리 검증 규칙 을 바 꾸 어도 곧 해 결 될 것 이다.나 는 오히려 시간 을 어떻게 거꾸로 걸 느 냐 에 쓰 느 니 차라리 내부 에서 외 걸 이 를 제공 하고 게임 의 방법 을 참고 하 는 것 이 낫다 고 생각한다. 12306 은 내부 에서 원 클릭 으로 표를 뺏 는 기능 을 제공 하 는 것 이 낫다. 차 를 선택 하고 승객 을 선택 하 며 다음 인증 코드 를 제출 하고 다시 확인 하 는 등 복잡 한 과정 을 거치 지 않 아 도 된다.사용자 가 미리 설정 한 규칙 에 따라 원 키 로 표를 뺏 는 것 (내부 원 키 로 표를 뺏 는 기능 이 외부 개표 소프트웨어 보다 표를 뺏 을 확률 이 높다 는 것 을 보증 해 야 한다). 그러면 좀 더 진실 해 야 한다. 어떻게 생각 하 세 요?응원 의 하트
부호http://blog.csdn.net/tang9140。
KEY 암호 화 알고리즘 에 관 한 JAVA 버 전 구현 코드 를 첨부 하여 관심 있 는 분 들 은 보 실 수 있 습 니 다.
public class DynamicJsUtil {
    
    private static String keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    
    static class Base32 {
        
        private static int delta = 0x9E3779B8;
        
        public static String longArrayToString(int[] data, boolean includeLength) {
            int length = data.length;
            int n = (length - 1) << 2;
            if (includeLength) {
                int m = data[length - 1];
                if ((m < n - 3) || (m > n))
                    return null;
                n = m;
            }
            
            StringBuilder sb = new StringBuilder("");
            for (int i = 0; i < length; i++) {
                int i0 = data[i] & 0xff;
                int i8 = data[i] >>> 8 & 0xff;
                int i16 = data[i] >>> 16 & 0xff;
                int i24 = data[i] >>> 24 & 0xff;
                if (i0 != 0)
                    sb.append((char)i0);
                if (i8 != 0)
                    sb.append((char)i8);
                if (i16 != 0)
                    sb.append((char)i16);
                if (i24 != 0)
                    sb.append((char)i24);
            }
            
            String result;
            if (includeLength) {
                result = sb.substring(0, n);
            }
            else
                result = sb.toString();
            return result;
        }
        
        public static int[] stringToLongArray(String str, boolean includeLength) {
            int length = str.length();
            int arrsize = length % 4 == 0 ? length / 4 : length / 4 + 1;
            int[] result = new int[arrsize];
            for (int i = 0; i < length; i += 4) {
                if (i + 4 > length) {
                    int char8 = i + 1 >= length ? 0 : str.charAt(i + 1) << 8;
                    int char16 = i + 2 >= length ? 0 : str.charAt(i + 1) << 16;
                    int char24 = i + 3 >= length ? 0 : str.charAt(i + 1) << 24;
                    result[i >> 2] = str.charAt(i) | char8 | char16 | char24;
                }
                else
                    result[i >> 2] =
                        str.charAt(i) | str.charAt(i + 1) << 8 | str.charAt(i + 2) << 16 | str.charAt(i + 3) << 24;
            }
            if (includeLength) {
                int[] newArr = new int[arrsize + 1];
                System.arraycopy(result, 0, newArr, 0, arrsize);
                newArr[arrsize] = length;
                result = newArr;
            }
            return result;
        }
        
        public static String encrypt(String str, String key) {
            if (str == "") {
                return "";
            }
            int[] v = stringToLongArray(str, true);
            int[] k = stringToLongArray(key, false);
            if (k.length < 4) {
                int[] newArr = new int[4];
                System.arraycopy(k, 0, newArr, 0, k.length);
                k = newArr;
            }
            int n = v.length - 1;
            int z = v[n], y = v[0];
            int mx, e, p, sum = 0;
            int q = 6 + 52 / (n + 1);
            while (0 < q--) {
                sum = sum + delta & 0xffffffff;
                e = sum >>> 2 & 3;
                for (p = 0; p < n; p++) {
                    y = v[p + 1];
                    mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
                    z = v[p] = v[p] + mx & 0xffffffff;
                }
                y = v[0];
                mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
                z = v[n] = v[n] + mx & 0xffffffff;
            }
            return longArrayToString(v, false);
        };
    }
    
    public static String encode32(String input) {
        input = escape(input);
        StringBuilder output = new StringBuilder();
        int length = input.length();
        int chr1, chr2, chr3;
        int enc1, enc2, enc3, enc4;
        int i = 0;
        do {
            chr1 = input.charAt(i++);
            enc1 = chr1 >> 2;
            output.append(keyStr.charAt(enc1));
            
            if (i < length) {
                chr2 = input.charAt(i++);
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                output.append(keyStr.charAt(enc2));
                
                if (i < length) {
                    chr3 = input.charAt(i++);
                    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                    enc4 = chr3 & 63;
                    output.append(keyStr.charAt(enc3)).append(keyStr.charAt(enc4));
                }
                else {
                    enc3 = ((chr2 & 15) << 2) | (0 >> 6);
                    output.append(keyStr.charAt(enc3)).append(keyStr.charAt(64));
                }
            }
            else {
                enc2 = ((chr1 & 3) << 4) | (0 >> 4);
                output.append(keyStr.charAt(enc2)).append(keyStr.charAt(64)).append(keyStr.charAt(64));
            }
        } while (i < length);
        return output.toString();
    }
    
    static String bin216(String s) {
        s += "";
        String output = "";
        int l = s.length();
        for (int i = 0; i < l; i++) {
            char c = s.charAt(i);
            String temp = Integer.toString(c, 16);
            output += temp.length() < 2 ? "0" + temp : temp;
        }
        return output;
    }
    
    public static String escape(String src) {
        char j;
        StringBuffer tmp = new StringBuffer(src.length() * 2);
        for (int i = 0; i < src.length(); i++) {
            j = src.charAt(i);
            if (Character.isDigit(j) || Character.isLowerCase(j) || Character.isUpperCase(j))
                tmp.append(j);
            else if (j < 256) {
                if (j == '*' || j == '@' || j == '-' || j == '_' || j == '+' || j == '.' || j == '/') {
                    tmp.append(j);
                }
                else {
                    tmp.append("%");
                    if (j < 16)
                        tmp.append("0");
                    tmp.append(Integer.toString(j, 16).toUpperCase());
                }
            }
            else {
                tmp.append("%u");
                tmp.append(Integer.toString(j, 16));
            }
        }
        return tmp.toString();
    }
    
    /**
     *       value 
     * @param key
     * @return
     */
    public static String getRandomParamValue(String key) {
        return encode32(DynamicJsUtil.bin216(Base32.encrypt("1111", key)));
    }
}

저작권 성명: 본 고 는 블 로 거들 이 창작 한 글 로 블 로 거들 의 허락 없 이 전재 할 수 없다.

좋은 웹페이지 즐겨찾기