Skip to content

风控基础概念(写给小白的入门指南)

为了更通俗地理解“风控”,我们可以把它想象成一家银行或超市的安保系统。它的核心目的就是区分正常顾客(真实用户)捣乱分子(爬虫、黑产、羊毛党)

技术风控通常从三个维度来“审视”一个用户:

1. 你是谁?(环境风控)

这是安保的第一道关卡,检查你的“身份证”和“交通工具”。

  • IP地址(住址)
    • 原理:IP就像你的家庭住址。
    • 例子:如果安保发现一秒钟内有1000个人都从同一个“住址”(IP)挤进超市,那肯定有问题(可能是数据中心IP或代理IP)。
  • 设备指纹(生物特征)
    • 原理:就像人的指纹或DNA。即使你换了衣服(清除浏览器Cookie),你的身高、体重、虹膜(显卡型号、屏幕分辨率、电池电量等硬件信息)是很难改变的。
    • 例子:网站发现一个账号虽然换了名字,但“指纹”和之前被封号的坏蛋一模一样,就会立刻警觉。
  • 运行环境(伪装检测)
    • 原理:检查你是不是在“穿玩偶服”或者是“机器人”。
    • 例子:手机上的模拟器、电脑上的无头浏览器(Headless Chrome,一种没有界面的浏览器),就像是穿着人类衣服的机器人,很容易被识别出来。

2. 你在做什么?(行为风控)

这是安保在监控摄像头里看你的表现。

  • 访问频率(手速)
    • 原理:正常人看网页需要时间阅读。
    • 例子:如果你一秒钟打开了50个商品页面,或者24小时不睡觉一直在刷新,这显然是机器人(爬虫)的手速。
  • 操作轨迹(动作)
    • 原理:人类操作鼠标和触摸屏是有惯性和抖动的。
    • 例子:机器人的鼠标移动往往是笔直的直线,点击位置非常精确;而人类的手会抖动,轨迹是弯曲的。
  • 访问逻辑(行为逻辑)
    • 原理:正常人逛淘宝是:首页 -> 搜索 -> 详情页 -> 评价。
    • 例子:爬虫往往为了省事,直接“瞬移”到详情页(直接请求API接口),跳过了前面的步骤,这就不符合人类逻辑。

3. 你在说什么?(代码与协议风控)

这是安保和你对“暗号”。

  • 代码混淆(鬼画符)
    • 原理:网站把给浏览器看的代码写得乱七八糟,变量名用a, b, c或者乱码代替。
    • 例子:本来代码写的是“点击按钮领取红包”,混淆后变成了“当X触发Y时执行Z函数”,让人类程序员很难读懂,增加破解难度。
  • 加密参数(对暗号)
    • 原理:在发送请求时,必须带上一个经过特定算法计算的“签名”(Sign)或“令牌”(Token)。
    • 例子:你想查快递信息,服务器问:“天王盖地虎?”如果你答不上来“宝塔镇河妖”(正确的加密结果),服务器就拒绝给你数据。

Web风控

Web风控技术介绍

  • 浏览器指纹识别: 通过收集浏览器特征(Canvas、WebGL、字体列表、插件信息等)生成唯一标识
  • TLS指纹识别: 分析TLS/SSL握手过程中的特征,识别自动化工具
  • HTTP/2指纹: 检测HTTP/2协议的实现差异,区分真实浏览器和模拟请求
  • JavaScript挑战: 通过复杂的JavaScript代码执行环境检测
  • 鼠标轨迹分析: 分析鼠标移动、点击的时间和路径特征
  • 键盘输入检测: 监测键盘输入的速度和节奏
  • 页面行为分析: 追踪页面滚动、停留时间、元素交互等行为

如何阻止Web爬虫

  1. 部署WAF(Web应用防火墙)使用云WAF服务如Cloudflare、AWS WAF等:

    • 配置IP黑白名单
    • 设置访问频率限制
    • 启用Bot管理功能
  2. 实施多层验证机制

    • 首次访问时进行JavaScript环境检测
    • 关键操作触发滑块验证或行为验证
    • 异常流量触发图片验证码
  3. 使用反调试技术

    jsx
    // 检测开发者工具
    (function() {
        const devtools = /./;
        devtools.toString = function() {
            this.opened = true;
        }
        console.log('%c', devtools);
        if(devtools.opened) {
            window.location.href = 'about:blank';
        }
    })();
    
    // 禁用右键和F12
    document.oncontextmenu = () => false;
    document.onkeydown = (e) => {
        if(e.key === 'F12') return false;
    };
  4. 动态加密通信

    jsx
    // 动态生成加密密钥
    function encryptRequest(data) {
        const key = generateDynamicKey();
        const encrypted = CryptoJS.AES.encrypt(
            JSON.stringify(data), 
            key
        ).toString();
        return {
            payload: encrypted,
            timestamp: Date.now(),
            sign: generateSignature(encrypted)
        };
    }

如何突破Web风控

  1. 使用真实浏览器环境

    • Selenium + undetected-chromedriver
    • Puppeteer Stealth插件
    • Playwright的反检测模式
    python
    from undetected_chromedriver import Chrome
    from selenium.webdriver.common.by import By
    
    options = Chrome.ChromeOptions()
    options.add_argument('--disable-blink-features=AutomationControlled')
    driver = Chrome(options=options)
    
    # 修改webdriver属性
    driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
        'source': '''
            Object.defineProperty(navigator, 'webdriver', {
                get: () => undefined
            })
        '''
    })
  2. 模拟真实用户行为

    python
    import random
    import time
    
    def human_like_mouse_move(driver, element):
        # 模拟人类鼠标移动轨迹
        action = ActionChains(driver)
        # 添加随机停顿和曲线移动
        for _ in range(random.randint(3, 8)):
            action.move_by_offset(
                random.randint(-5, 5), 
                random.randint(-5, 5)
            )
            action.pause(random.uniform(0.01, 0.05))
        action.move_to_element(element)
        action.click()
        action.perform()
        
    def human_like_typing(element, text):
        for char in text:
            element.send_keys(char)
            time.sleep(random.uniform(0.1, 0.3))
  3. 使用代理池和浏览器指纹伪造

    python
    from selenium_profiles.webdriver import Chrome
    from selenium_profiles.profiles import profiles
    
    # 使用随机浏览器配置文件
    profile = profiles.Windows()
    driver = Chrome(profile=profile)
    
    # 配置代理
    profile.proxy = {
        'http': 'http://proxy:port',
        'https': 'https://proxy:port'
    }
  4. 逆向分析加密算法

    • 使用Chrome DevTools分析网络请求
    • 利用jadx、IDA Pro等工具逆向JavaScript
    • hook关键函数获取加密参数
    jsx
    // 使用Tampermonkey hook加密函数
    (function() {
        const originalEncrypt = window.encryptFunction;
        window.encryptFunction = function(...args) {
            console.log('加密参数:', args);
            const result = originalEncrypt.apply(this, args);
            console.log('加密结果:', result);
            return result;
        };
    })();

Android风控

Android风控技术介绍

  • 设备指纹采集: 收集IMEI、Android ID、MAC地址、设备型号等硬件信息
  • Root检测: 检测设备是否已Root,防止Hook和篡改
  • 模拟器检测: 识别Genymotion、夜神、雷电等Android模拟器
  • VPN/代理检测: 检测网络是否使用了VPN或代理
  • 多开检测: 识别应用分身、多开软件
  • Hook框架检测: 检测Xposed、Frida、VirtualXposed等Hook工具
  • 内存检查: 检测内存中是否存在可疑进程或注入
  • 签名校验: 验证APK签名,防止应用被二次打包
  • 代码混淆: 使用ProGuard、R8等工具混淆代码
  • SO加固: 对Native层代码进行加密和保护

如何阻止Android端攻击

  1. 实施Root检测

    java
    public class RootDetection {
        public static boolean isDeviceRooted() {
            return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
        }
        
        private static boolean checkRootMethod1() {
            String buildTags = android.os.Build.TAGS;
            return buildTags != null && buildTags.contains("test-keys");
        }
        
        private static boolean checkRootMethod2() {
            String[] paths = {
                "/system/app/Superuser.apk",
                "/sbin/su",
                "/system/bin/su",
                "/system/xbin/su"
            };
            for (String path : paths) {
                if (new File(path).exists()) return true;
            }
            return false;
        }
        
        private static boolean checkRootMethod3() {
            Process process = null;
            try {
                process = Runtime.getRuntime().exec(new String[]{"which", "su"});
                BufferedReader in = new BufferedReader(
                    new InputStreamReader(process.getInputStream())
                );
                return in.readLine() != null;
            } catch (Throwable t) {
                return false;
            } finally {
                if (process != null) process.destroy();
            }
        }
    }
  2. 模拟器检测

    java
    public class EmulatorDetection {
        public static boolean isEmulator() {
            return (Build.FINGERPRINT.startsWith("generic")
                || Build.FINGERPRINT.startsWith("unknown")
                || Build.MODEL.contains("google_sdk")
                || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86")
                || Build.MANUFACTURER.contains("Genymotion")
                || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
                || "google_sdk".equals(Build.PRODUCT));
        }
        
        private static boolean checkEmulatorFiles() {
            String[] known_files = {
                "/dev/socket/qemud",
                "/dev/qemu_pipe",
                "/system/lib/libc_malloc_debug_qemu.so",
                "/sys/qemu_trace"
            };
            for (String file : known_files) {
                if (new File(file).exists()) {
                    return true;
                }
            }
            return false;
        }
    }
  3. 检测Hook框架

    java
    public class HookDetection {
        public static boolean isXposedActive() {
            try {
                throw new Exception();
            } catch (Exception e) {
                for (StackTraceElement stackTraceElement : e.getStackTrace()) {
                    if (stackTraceElement.getClassName().contains("de.robv.android.xposed"))
                        return true;
                }
            }
            
            // 检查已安装应用
            List<ApplicationInfo> apps = context.getPackageManager()
                .getInstalledApplications(PackageManager.GET_META_DATA);
            for (ApplicationInfo app : apps) {
                if (app.packageName.equals("de.robv.android.xposed.installer") ||
                    app.packageName.equals("com.saurik.substrate")) {
                    return true;
                }
            }
            return false;
        }
        
        public static boolean isFridaRunning() {
            String[] frida_servers = {
                "frida-server",
                "frida-agent",
                "gadget"
            };
            
            try {
                Process process = Runtime.getRuntime().exec("ps");
                BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream())
                );
                String line;
                while ((line = reader.readLine()) != null) {
                    for (String server : frida_servers) {
                        if (line.contains(server)) {
                            return true;
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    }
  4. 使用加固服务

    • 360加固保
    • 腾讯乐固
    • 梆梆加固
    • 爱加密
  5. SSL Pinning

    java
    // 使用OkHttp实现证书固定
    CertificatePinner certificatePinner = new CertificatePinner.Builder()
        .add("yourapi.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
        .build();
    
    OkHttpClient client = new OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .build();

如何突破Android风控

  1. 使用Frida进行动态Hook

    jsx
    // Frida脚本绕过Root检测
    Java.perform(function() {
        var RootDetection = Java.use('com.example.RootDetection');
        
        RootDetection.isDeviceRooted.implementation = function() {
            console.log('Root检测被调用,返回false');
            return false;
        };
        
        // Hook文件存在性检查
        var File = Java.use('java.io.File');
        File.exists.implementation = function() {
            var path = this.getAbsolutePath();
            if (path.indexOf('su') >= 0 || path.indexOf('Superuser') >= 0) {
                console.log('拦截su文件检查: ' + path);
                return false;
            }
            return this.exists();
        };
    });
  2. 使用VirtualXposed或太极

    • 无需Root即可使用Xposed模块
    • 在虚拟环境中运行目标应用
    • 使用JustTrustMe等模块绕过SSL Pinning
  3. 模拟器伪装

    python
    # 修改模拟器Build属性
    adb shell su
    mount -o rw,remount /system
    vi /system/build.prop
    
    # 修改以下参数
    ro.build.fingerprint=真实设备指纹
    ro.product.manufacturer=Xiaomi
    ro.product.model=MI 10
    ro.build.tags=release-keys
  4. 绕过SSL Pinning

    jsx
    // 使用Frida绕过OkHttp的证书固定
    Java.perform(function() {
        var CertificatePinner = Java.use('okhttp3.CertificatePinner');
        
        CertificatePinner.check.overload('java.lang.String', 'java.util.List')
            .implementation = function(hostname, peerCertificates) {
            console.log('绕过证书固定检查: ' + hostname);
            return;
        };
        
        // 信任所有证书
        var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
        var SSLContext = Java.use('javax.net.ssl.SSLContext');
        
        var TrustManager = Java.registerClass({
            name: 'com.example.TrustManager',
            implements: [X509TrustManager],
            methods: {
                checkClientTrusted: function(chain, authType) {},
                checkServerTrusted: function(chain, authType) {},
                getAcceptedIssuers: function() { return []; }
            }
        });
    });
  5. 使用反编译和重打包

    bash
    # 使用apktool反编译
    apktool d app.apk -o app_decompiled
    
    # 修改smali代码,去除检测逻辑
    # 编辑 app_decompiled/smali/com/example/RootDetection.smali
    
    # 重新打包
    apktool b app_decompiled -o app_modified.apk
    
    # 签名
    jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 \
      -keystore my-release-key.keystore app_modified.apk alias_name

iOS风控

iOS风控技术介绍

  • 越狱检测: 检测设备是否已越狱,防止运行时篡改
  • 设备指纹: 收集IDFV、IDFA、设备型号等信息
  • 代码签名验证: 验证应用签名,防止被修改
  • 反调试: 检测是否被LLDB等调试器附加
  • 反注入: 检测动态库注入(如Substrate、Frida)
  • 完整性校验: 检查可执行文件和资源文件的完整性
  • SSL Pinning: 证书固定防止中间人攻击
  • 代码混淆: 使用LLVM混淆或商业混淆工具
  • 字符串加密: 加密敏感字符串和配置信息

如何阻止iOS端攻击

  1. 越狱检测

    swift
    import UIKit
    
    class JailbreakDetection {
        static func isJailbroken() -> Bool {
            return checkSuspiciousFiles() || 
                   checkSuspiciousApps() || 
                   checkWritePermission() ||
                   checkCydia()
        }
        
        private static func checkSuspiciousFiles() -> Bool {
            let paths = [
                "/Applications/Cydia.app",
                "/Library/MobileSubstrate/MobileSubstrate.dylib",
                "/bin/bash",
                "/usr/sbin/sshd",
                "/etc/apt",
                "/private/var/lib/apt/"
            ]
            
            for path in paths {
                if FileManager.default.fileExists(atPath: path) {
                    return true
                }
            }
            return false
        }
        
        private static func checkSuspiciousApps() -> Bool {
            guard let url = URL(string: "cydia://package/com.example.package") else {
                return false
            }
            return UIApplication.shared.canOpenURL(url)
        }
        
        private static func checkWritePermission() -> Bool {
            let testPath = "/private/jailbreak_test.txt"
            do {
                try "test".write(toFile: testPath, atomically: true, encoding: .utf8)
                try FileManager.default.removeItem(atPath: testPath)
                return true
            } catch {
                return false
            }
        }
        
        private static func checkCydia() -> Bool {
            let cydiaPath = "/Applications/Cydia.app"
            return FileManager.default.fileExists(atPath: cydiaPath)
        }
    }
  2. 反调试保护

    objectivec
    #import <sys/sysctl.h>
    #import <unistd.h>
    
    // 检测调试器
    BOOL isDebuggerAttached() {
        struct kinfo_proc info;
        size_t info_size = sizeof(info);
        int name[4];
        
        name[0] = CTL_KERN;
        name[1] = KERN_PROC;
        name[2] = KERN_PROC_PID;
        name[3] = getpid();
        
        if (sysctl(name, 4, &info, &info_size, NULL, 0) == -1) {
            return NO;
        }
        
        return (info.kp_proc.p_flag & P_TRACED) != 0;
    }
    
    // 防止被ptrace附加
    void disablePtrace() {
        #import <dlfcn.h>
        #import <sys/types.h>
        
        typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
        void *handle = dlopen(NULL, RTLD_GLOBAL | RTLD_NOW);
        ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
        ptrace_ptr(31, 0, 0, 0); // PT_DENY_ATTACH = 31
        dlclose(handle);
    }
  3. 检测注入和Hook

    swift
    import MachO
    
    class InjectionDetection {
        static func detectInjection() -> Bool {
            let suspiciousLibraries = [
                "MobileSubstrate",
                "Substrate",
                "Frida",
                "FridaGadget",
                "libcycript"
            ]
            
            var count: UInt32 = 0
            guard let images = _dyld_image_count() as UInt32? else {
                return false
            }
            
            for i in 0..<images {
                guard let imageName = _dyld_get_image_name(i) else {
                    continue
                }
                let name = String(cString: imageName)
                
                for library in suspiciousLibraries {
                    if name.contains(library) {
                        return true
                    }
                }
            }
            return false
        }
    }
  4. SSL Pinning实现

    swift
    import Security
    
    class SSLPinning: NSObject, URLSessionDelegate {
        let certificates: [SecCertificate]
        
        init(certificates: [SecCertificate]) {
            self.certificates = certificates
        }
        
        func urlSession(_ session: URLSession,
                       didReceive challenge: URLAuthenticationChallenge,
                       completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            
            guard let serverTrust = challenge.protectionSpace.serverTrust else {
                completionHandler(.cancelAuthenticationChallenge, nil)
                return
            }
            
            let serverCertificates = (0..<SecTrustGetCertificateCount(serverTrust)).compactMap {
                SecTrustGetCertificateAtIndex(serverTrust, $0)
            }
            
            for serverCert in serverCertificates {
                for localCert in certificates {
                    if serverCert == localCert {
                        completionHandler(.useCredential, URLCredential(trust: serverTrust))
                        return
                    }
                }
            }
            
            completionHandler(.cancelAuthenticationChallenge, nil)
        }
    }
  5. 代码混淆和加密

    • 使用Hikari LLVM混淆器
    • 使用商业加固方案(如爱加密、梆梆)
    • 字符串加密和函数名混淆

如何突破iOS风控

  1. 使用Frida绕过越狱检测

    jsx
    // Frida脚本绕过越狱检测
    if (ObjC.available) {
        // Hook文件存在性检查
        var NSFileManager = ObjC.classes.NSFileManager;
        Interceptor.attach(NSFileManager['- fileExistsAtPath:'].implementation, {
            onEnter: function(args) {
                var path = ObjC.Object(args[2]).toString();
                if (path.indexOf('Cydia') >= 0 || 
                    path.indexOf('MobileSubstrate') >= 0 ||
                    path.indexOf('/bin/bash') >= 0) {
                    console.log('拦截文件检查: ' + path);
                    args[2] = ObjC.classes.NSString.stringWithString_('/tmp');
                }
            }
        });
        
        // Hook canOpenURL
        var UIApplication = ObjC.classes.UIApplication;
        Interceptor.attach(UIApplication['- canOpenURL:'].implementation, {
            onEnter: function(args) {
                var url = ObjC.Object(args[2]).toString();
                if (url.indexOf('cydia') >= 0) {
                    console.log('拦截URL检查: ' + url);
                    this.hooked = true;
                }
            },
            onLeave: function(retval) {
                if (this.hooked) {
                    retval.replace(0);
                }
            }
        });
        
        // Hook sysctl检测调试器
        var sysctl = Module.findExportByName(null, 'sysctl');
        Interceptor.attach(sysctl, {
            onLeave: function(retval) {
                var info = Memory.readPointer(this.context.x2);
                var p_flag = Memory.readU32(info.add(8));
                if ((p_flag & 0x800) !== 0) { // P_TRACED
                    Memory.writeU32(info.add(8), p_flag & ~0x800);
                }
            }
        });
    }
  2. 使用Objection自动化绕过

    bash
    # 安装Objection
    pip3 install objection
    
    # 注入应用
    objection --gadget "com.example.app" explore
    
    # 绕过越狱检测
    ios jailbreak disable
    
    # 绕过SSL Pinning
    ios sslpinning disable
    
    # Hook方法
    ios hooking watch method "-[ClassName methodName:]"
  3. 重签名和注入

    bash
    # 使用insert_dylib注入动态库
    git clone https://github.com/Tyilo/insert_dylib.git
    cd insert_dylib
    make
    
    # 解密应用(如果需要)
    # 使用frida-ios-dump或Clutch
    
    # 注入FridaGadget
    ./insert_dylib @executable_path/FridaGadget.dylib \
      Payload/App.app/App \
      --output Payload/App.app/App_patched
    
    # 复制FridaGadget到应用目录
    cp FridaGadget.dylib Payload/App.app/
    
    # 重签名
    codesign -f -s "iPhone Developer: Your Name" Payload/App.app
    
    # 打包
    zip -r app_modified.ipa Payload
  4. 绕过SSL Pinning

    jsx
    // 使用Frida绕过SSL Pinning
    if (ObjC.available) {
        // Method 1: Hook NSURLSession
        var NSURLSession = ObjC.classes.NSURLSession;
        Interceptor.attach(
            NSURLSession['- URLSession:didReceiveChallenge:completionHandler:'].implementation,
            {
                onEnter: function(args) {
                    var completionHandler = new ObjC.Block(args[4]);
                    var credential = ObjC.classes.NSURLCredential.credentialForTrust_(args[3]);
                    completionHandler.implementation(0, credential); // NSURLSessionAuthChallengeUseCredential
                }
            }
        );
        
        // Method 2: Hook SecTrustEvaluate
        var SecTrustEvaluate = Module.findExportByName('Security', 'SecTrustEvaluate');
        Interceptor.replace(SecTrustEvaluate, new NativeCallback(function(trust, result) {
            Memory.writeU8(result, 1); // kSecTrustResultProceed
            return 0; // errSecSuccess
        }, 'int', ['pointer', 'pointer']));
    }
  5. 使用越狱检测绕过工具

    • A-Bypass: 通用越狱检测绕过工具
    • Liberty Lite: Cydia插件,绕过越狱检测
    • Shadow: 隐藏越狱状态
    • FlyJB X: 绕过越狱检测

风控对抗总结

攻防对抗的持续演进

  • 防御方: 需要持续更新检测策略,采用多层防御,结合设备指纹、行为分析、机器学习等技术
  • 攻击方: 需要深入理解系统机制,掌握逆向分析、动态调试、代码注入等技术
  • 平衡点: 风控策略需要在安全性和用户体验之间找到平衡,过度的防护会影响正常用户

最佳实践建议

  • 采用纵深防御策略,不要依赖单一防护手段
  • 重要数据和逻辑放在服务端处理
  • 定期更新和升级防护机制
  • 建立监控和告警体系,及时发现异常
  • 对敏感操作进行二次验证
  • 使用专业的安全加固服务
  • 遵守相关法律法规,合理使用风控技术
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.7.1