前后端是怎么验证身份的? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
ericgui
V2EX    程序员

前后端是怎么验证身份的?

  •  
  •   ericgui 2018-01-25 07:56:58 +08:00 11705 次点击
    这是一个创建于 2886 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我前几天看了一个写 RESTful API 的教程

    前端用 React 后端用 Express JS 和 MongoDB

    后端写了几个 API,然后前端是一个单页应用( SPA ),用 fetch 来请求 api,获取数据。

    看着相当好。

    后来我就觉得不对:这 api 连个验证都没有,岂不是谁都可以来请求这个 api。

    这数据也不是机密数据吧,但如果任何人都可以请求 api,拿岂不是 api 暴露给任何人了,用个爬虫,轻松就把数据下载光了。流量和带宽,该多费钱。。。。。

    所以能否请高人介绍一下,这前后端分离,一般是怎样验证这个“前端”是自己的、官方的,而不是爬虫或者什么其他人写的非官方前端。尤其是写 app 的时候,app 显然算是“前端”或者“客户端”,那么服务端怎么验证这个请求是从自己官方的客户端发出的呢?

    JSON Web Tokens ( jwt )?

    谢谢

    56 条回复    2018-07-31 16:07:25 +08:00
    MrVito
        1
    MrVito  
       2018-01-25 07:59:44 +08:00 via Android
    签名?
    toku
        2
    toku  
       2018-01-25 08:00:06 +08:00 via iPhone
    token ?
    conn4575
        3
    conn4575  
       2018-01-25 08:05:31 +08:00 via Android
    先登录获取合法的 token,然后后面的交互都先验证这个 token,token 每隔一小时左右更新一次,防止失效,参考 jwt 实现
    CEBBCAT
        4
    CEBBCAT  
       2018-01-25 08:06:00 +08:00 via Android
    这,我记得谷歌翻译 Web 版调用的 API 有一个参数是在页面内用 JS 生成的,除此之外好像也没啥验证了
    Mutoo
        5
    Mutoo  
       2018-01-25 08:11:14 +08:00
    对网页前端来说,并没有‘自己官方的客户端’这个说法,你的代码是运行在 UserAgent 上的,通常是浏览器。而爬虫也是一种 UserAgent,那么用户就可以授权 UserAgent 去获取数据。你能做的是对用户鉴权,而不是对 UserAgent。
    ericgui
        6
    ericgui  
    OP
       2018-01-25 08:11:28 +08:00
    @conn4575 谢谢
    ericgui
        7
    ericgui  
    OP
       2018-01-25 08:16:39 +08:00
    @Mutoo 这教程里说的前后端分离,大意是后端就是服务器,expressjs 做的,前端是广义的,可以是一个单页应用 SPA,也可以是一个 app,或者一个什么其他的东西。总之,就是一个广义的前端对后端的 api 发起请求。

    请问“对用户鉴权”是这个意思么:用户登陆,后端 api 接收用户名和密码,如果匹配,就返回一个 token ?在 token 的有效期内,前端可以请求后端的 api,并且每次请求的时候,一起把 token 也发送回服务器,服务器要验证这个 token,如果验证通过,就返回相应的数据,如果验证不通过,就给一个 403 ( No Permission )?

    请问是这个意思么?谢谢
    zhlssg
        8
    zhlssg  
       2018-01-25 08:30:02 +08:00 via iPhone
    Session 也是一样的,不过是教验 cookie 而已,所以接口做不到绝对安全
    RqPS6rhmP3Nyn3Tm
        9
    RqPS6rhmP3Nyn3Tm  
       2018-01-25 08:30:47 +08:00 via iPhone
    我用的是 signature+token
    ericgui
        10
    ericgui  
    OP
       2018-01-25 08:35:36 +08:00
    @BXIA 请教,用的啥 node module ?什么库?
    fujie
        11
    fujie  
       2018-01-25 08:42:02 +08:00
    1. 前端发起 http 请求时有携带 cookie,
    2. 后端拿到此 cookie 对比服务器 session,有登陆则放过此请求,无登录,redirect 到登录页面。
    3. 登录页面,前端登录,后端比对用户名密码,成功则生成唯一标识符,放在 session,并且种入浏览器 cookie。
    另外,合法用户可以拿到自己合法的 cookie,用任何程序构造一个合法的客户端 http 请求。其他用户拿不到这个 cookie,
    因个人过失暴露 cookie 给其他人,属于用户个人的行为,比如你在网吧里登录 QQ,服务端没有办法不允许这样操作。而客户端的人应有安全意识,在公共场所及时清空 cookie,或者停止使用一切 [不随 session 关闭而 cookie 失效] 的应用。
    Mutoo
        12
    Mutoo  
       2018-01-25 09:04:08 +08:00
    @ericgui #7 是的。
    v2chou
        13
    v2chou  
       2018-01-25 09:14:54 +08:00
    我也有这种疑惑 比如在小程序中 接口都是请求的 没有登录的操作 怎么防止别人调用我的接口呢
    LeeSeoung
        14
    LeeSeoung  
       2018-01-25 09:18:07 +08:00
    防不了前端是不是自己的,但是可以针对一些特征返回脏数据,而不是直接拒绝返回,这样可以玩弄下初级爬虫的。。后台 API 加权限验证。
    swordne
        15
    swordne  
       2018-01-25 09:18:57 +08:00   3
    其实在 web 环境下,对用户的鉴权只能是不断提高伪造的成本,不能从根本上解决。
    前端环境太复杂,所有的东西都是暴漏的,所有的鉴权参数获取都是成本问题而非技术问题。
    oott123
        16
    oott123  
       2018-01-25 09:23:09 +08:00 via Android
    不关心前端是不是自己的,甚至鼓励别人来写前端~
    MyDaLin
        17
    MyDaLin  
       2018-01-25 09:28:08 +08:00
    都是高手,我路过的
    geelaw
        18
    geelaw  
       2018-01-25 09:28:31 +08:00
    在客户端由不受信第三方(比如用户)控制的情况下,不存在能够验证请求来自“自己的”前端的方法。

    如果客户端不受不受信第三方的控制比如你可以确保你的(二进制)代码只被分发商看到,而最终用户无法看到代码则可以在请求中使用密码学手段保证发送请求的程序“知道”某个秘密比如一个密钥。

    #7 的回复让我搞不明白楼主想要解决的问题,到底是验证发送请求的程序是自己的程序,还是使用程序的用户是在访问自己可以访问的数据。这是不同的问题。
    RqPS6rhmP3Nyn3Tm
        19
    RqPS6rhmP3Nyn3Tm  
       2018-01-25 09:28:52 +08:00 via iPhone
    @ericgui tornado ……
    miketeam
        20
    miketeam  
       2018-01-25 09:54:51 +08:00 via iPhone   1
    我目前是采用自己建 oauth2.0,登录一次,获取主 token,有效期一年。然后用这个 token 给每个 API 刷临时 token。这是我自己项目尝试。公司的项目用 cookie …
    miketeam
        21
    miketeam  
    /div>   2018-01-25 10:05:12 +08:00 via iPhone
    还有每个 API 有个时间请求的限制,1 秒
    houn1995
        22
    houn1995  
       2018-01-25 10:12:47 +08:00
    接口根据需求加认证权限,比如登陆之后才能用的,可以在登陆之后,返回前端一个 token,每次前端在请求的时候吧 token 放到 header 的验证中,这样后台判断这个 token 是否正确从而确定是否返回接口数据,通常 token 也会有个有效时间。
    qiutc
        23
    qiutc  
       2018-01-25 10:15:56 +08:00
    服务端 session 生成 sid 放在 cookie 中,登录时在返回的 Header Set-Cookie,之后的请求都会带上这个 cookie,服务端拿 cookie 对应 session 来验证。
    edisonchen
        24
    edisonchen  
       2018-01-25 10:41:06 +08:00
    对于这个问题,假设服务端用 JWT 等方式,我用账号密码登陆后可以获取到这个返回 token,然后我自己用我自己的这个 token 拿爬虫来调接口,这种方式算不算上文提到的:(---这个请求是从自己官方的客户端发出的呢?--)
    yifeng1212
        25
    yifeng1212  
       2018-01-25 10:43:32 +08:00
    总结楼上几位的评论:
    大体分为两种方法,

    1.API 鉴权,可以使用 cookie,或者登录 token。如果是开发 API,可以参考微信或微博的开发者平台 API,他们所有的接口都要通过 token 来鉴权

    2.API 访问限制,可以限制同一个 IP 请求次数,或者限制请求频率(每天限额,或者单位时间请求数有上限)

    不能做到绝对的安全,但能提高被不怀好意利用的成本。
    xkeyideal
        26
    xkeyideal  
       2018-01-25 11:03:33 +08:00
    说一下,我的做法:
    1、前端只做 UI 的展示层,登录,权限校验,均交给后端完成
    2、登录接入公司统一的登录,类似 OAuth 这种,贵司不可能没有吧?
    3、每个 api 都有相应的路由权限控制,权限控制系统由后端完成,类似做个 middleware 或拦截器
    4、访问 api 必须带着当前登录用户的 token,用于校验用户信息,并且查询该用户能够操作的数据,token 是一天变化一次,并没有使用 JWT 这类的开源实现,原因就是太重
    5、每行数据都只能有该数据权限的人才能访问和操作

    总结:前端是无设防的,不可以信任; 全部的校验都由后端完成
    NEETLEE
        27
    NEETLEE  
       2018-01-25 11:08:47 +08:00
    我司目前的项目就是用 jwt 做的验证,jwt 也有一定的局限,不过仅仅是避免被爬应该可以胜任的
    AlisaDestiny
        28
    AlisaDestiny  
       2018-01-25 11:10:40 +08:00
    添加拦截器。为每个请求都做验证。通过验证的请求才交路由处理器。
    wujunchuan2008
        29
    wujunchuan2008  
       2018-01-25 11:29:49 +08:00
    前端做验证只是为了用户体验,比如控制按钮的显示隐藏,单页应用的路由跳转等等。
    后端才是最终的保障。
    timwei
        30
    timwei  
       2018-01-25 11:43:02 +08:00
    @ericgui

    先不讨论爬虫,浏览器 XHR 有同源策略,除非你有配置 CORS 相关的 Header,其他人在浏览器中无法直接调用你的 API

    https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

    爬虫的话,即使你做了相关的验证还是很好破

    以前用 captcha 挺有用的,不过自从 Google Vision 之类的 OCR 服务方便使用后也不靠谱了

    辨识率挺高的
    hlwjia
        31
    hlwjia  
    PRO
       2018-01-25 11:48:03 +08:00 via iPhone
    首先,要了解传统方式(你一直在用的)里,后端是前端发来的请求的怎么验证身份的?

    如果这个都不清楚,就还是先补补基础
    StevenTong
        32
    StevenTong  
       2018-01-25 11:49:31 +08:00
    jwt token
    hlwjia
        33
    hlwjia  
    PRO
       2018-01-25 11:49:37 +08:00 via iPhone
    在身份验证上其实没有本质区别
    ericgui
        34
    ericgui  
    OP
       2018-01-25 13:45:49 +08:00 via Android
    @xkeyideal 目前是 password 来做校验。所以我也有点晕。有了 password,还要用 jwt 么?
    xkeyideal
        35
    xkeyideal  
       2018-01-25 14:11:32 +08:00
    @ericgui 我们的做法是使用 token,password 那是登录做的事情,如果接入了统一登录,系统是拿不到用户密码的,至于 jwt 我没用过,太重
    alinwu05
        36
    alinwu05  
       2018-01-25 14:28:51 +08:00
    API 没有权限控制,如果跳一跳被随便 Post 数据一样,必须有验证啊!只是好多教程是前端写的,不太熟悉后端,所以都忽略了这一块。之前我写过一篇科普文章,看完你就明白了。https://my.oschina.net/u/248080/blog/875243
    wizardoz
        37
    wizardoz  
       2018-01-25 14:47:04 +08:00
    楼主可以区别一下 Authentication 和 Authorization,一个是 password 干的事情一个是 token 干的事情,对于一个完整的业务来说,两者都是必须的,只是具体如何实现就很灵活了。比如 OAuth2 的话 Authentication 和 Authorization 就可能在不同的地方进行。
    pandaaa
        38
    pandaaa  
       2018-01-25 14:52:33 +08:00
    楼主可以 google 一下 Anti CSRF Token
    MaxTan
        39
    MaxTan  
       2018-01-25 15:11:43 +08:00
    v2 月经贴
    CoderGeek
        40
    CoderGeek  
       2018-01-25 15:17:25 +08:00 via iPhone
    jwt 签名+自定义 token 先过了签名验证再验证 token
    om6r5sqSGG9Magr0
        41
    om6r5sqSGG9Magr0  
       2018-01-25 15:20:21 +08:00
    oauth2 是干嘛滴?
    POPOEVER
        42
    POPOEVER  
       2018-01-25 15:21:59 +08:00
    @v2chou wx.login 的时候把用户 OpenID 解出来自己存库啊,小程序里校验 api 只是对微信侧的验证支持,你自己 request api 那边的安全只能自己写 user 表后用 OpenID 来比对校验然后放行
    ivydom
        43
    ivydom  
       2018-01-25 21:09:01 +08:00
    最好正好在做通用认证系统,简单说一下:

    具体可以看这里: https://github.com/Authing/authing

    ### 认证流程

    ![auth_uml]( http://usercontents.authing.cn/white_paper/authing_auth_uml.png)

    认证通过后,后端会生成基于 JWT 规范的 Token。客户端将 Token 放到 HTTP 协议中的```Authorzation```头中并加上标注```Bearer```即可进行登录验证。

    流程如下:

    - 用户使用用户名密码来请求服务器
    - 服务器进行验证用户的信息
    - 服务器通过验证发送给用户一个 token
    - 客户端存储 token,并在每次请求时附送上这个 token 值
    - 服务端验证 token 值,并返回数据

    #### JWT

    JWT 是由三段信息构成的,将这三段信息文本用.链接一起就构成了 JWT 字符串。就像这样:

    ```
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    ```

    ##### JWT 的构成
    第一部分我们称它为头部( header),第二部分我们称其为载荷( payload, 类似于飞机上承载的物品),第三部分是签证( signature).

    ###### header
    jwt 的头部承载两部分信息:

    - 声明类型,这里是 jwt
    - 声明加密的算法 通常直接使用 HMAC SHA256

    完整的头部就像下面这样的 JSON:

    ``` Javascript
    {
    'typ': 'JWT',
    'alg': 'HS256'
    }
    ```

    然后将头部进行 base64 加密(该加密是可以对称解密的),构成了第一部分.
    ```
    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    playload
    ```

    载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

    - 标准中注册的声明
    - 公共的声明
    - 私有的声明

    标准中注册的声明 (建议但不强制使用) :

    - ```iss```: jwt 签发者
    - ```sub```: jwt 所面向的用户
    - ```aud```: 接收 jwt 的一方
    - ```exp```: jwt 的过期时间,这个过期时间必须要大于签发时间
    - ```nbf```: 定义在什么时间之前,该 jwt 都是不可用的.
    - ```iat```: jwt 的签发时间
    - ```jti```: jwt 的唯一身份标识,主要用来作为一次性 token,从而回避重放攻击。


    **公共的声明 :**

    公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

    ** 私有的声明 :**

    私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为 base64 是对称解密的,意味着该部分信息可以归类为明文信息。

    定义一个 payload:

    ``` Javascript
    {
    "sub": "1234567890",
    "name": "John Doe",
    "admin": true
    }
    ```

    然后将其进行 base64 加密,得到 Jwt 的第二部分。

    ```
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
    ```

    **signature**

    jwt 的第三部分是一个签证信息,这个签证信息由三部分组成:

    - header (base64 后的)
    - payload (base64 后的)
    - secret

    这个部分需要 base64 加密后的 header 和 base64 加密后的 payload 使用.连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 jwt 的第三部分。

    ``` Javascript
    var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

    var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    ```
    将这三部分用.连接成一个完整的字符串,构成了最终的 jwt:

    ```
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    ```

    注意:secret 是保存在服务器端的,jwt 的签发生成也是在服务器端的,secret 就是用来进行 jwt 的签发和 jwt 的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个 secret,那就意味着客户端是可以自我签发 jwt 了。
    Zzde
        44
    Zzde  
       2018-01-25 21:13:44 +08:00
    我的做法(小程序没有登陆)

    和前端协商一个固定的 Token
    每次请求以
    * sign(md5(token+time)
    * time
    为参数验证身份

    time 有效时间为 10s (其实可以更短)
    ai277014717
        45
    ai277014717  
       2018-01-26 10:55:42 +08:00
    看过一个源码,每次调用接口会发送两个请求,第一鉴定 session,第二个头里面包含有效的 session。session 过期逻辑可以自己设置,访问量,ip,时间,推出登陆之类的。
    RorschachZZZ
        46
    RorschachZZZ  
       2018-01-26 12:09:06 +08:00
    1 登录发 token。2 前端每次请求带着 token。3 后端在逻辑前面验证 token。
    ericgui
        47
    ericgui  
    OP
       2018-01-27 00:26:36 +08:00
    @ai277014717 能分享一下源码么? github url ?
    Lullaby
        48
    Lullaby  
       2018-01-27 13:15:16 +08:00
    令牌桶 & IP 频次记录
    ai277014717
        49
    ai277014717  
       2018-01-27 15:43:23 +08:00
    @ericgui 我看的是 parse-server-ios-sdk https://github.com/parse-community/Parse-SDK-iOS-OSX
    还有 js 和安卓 php 等等的版本。
    ericgui
        50
    ericgui  
    OP
       2018-01-28 11:25:00 +08:00
    @ai277014717 感谢分享,这个 Parse 是干什么的?看了半天没看明白啥意思
    sothx
        51
    sothx  
       2018-01-29 05:52:48 +08:00 via Android
    目前很多开放平台的的权限验证基本都是 token,也可以用 session ( cookie )
    注意下安全方面的设置就差不多了。
    前端也可以做权限鉴权,根据用户的权限,加载对应权限的路由,虽然是不可信的,但是也在一定程度上提高了安全。
    ai277014717
        52
    ai277014717  
       2018-01-29 09:39:31 +08:00
    @ericgui leancloud 就是模仿 parse 的是 Facebook 开源的一套产品,主要是把 HTTP 部分封装,客户端再也不用调用接口了,还有集成推送,云编码等功能。从 server 到客户端 sdk,很有学习的价值。
    YMB
        53
    YMB  
       2018-02-03 16:05:11 +08:00   1
    我们公司的这部分服务端就是我编码的,采用的模式是较为普遍的 Access Token,参考了微信的部分机制,过段时间有时间准备调研更优的实现,准备做一个更加灵活的架构。有一些文章楼主可以参考下: https://mengkang.net/625.html
    ericgui
        54
    ericgui  
    OP
       2018-02-04 02:03:22 +08:00
    @YMB 感谢!
    mingyun
        55
    mingyun  
       2018-06-09 19:58:31 +08:00
    @ivydom 很详细,star 了
    mingszu
        56
    mingszu  
       2018-07-31 16:07:25 +08:00
    说说我的看法:

    首先 restful 也是基于 HTTP 协议,而 HTTP 事务本来就是无状态的,也就是每一次请求与响应都是相互独立的,而身份认证那些都是慢慢扩展出来的

    认证方法
    (一) HTTP 请求头例如 Authorization,用于存储用户名与密码来实现用户验证,就算是 restful 也是 http,你也可以每次都在请求头上带上你的用户名密码来验证,但是每次都传输用户名密码太不安全了
    (二) session-cookie 模式:用户第一次登陆验证通过服务器将用户信息存储于 session 中,然后将 session 存储在本地服务器中,将 sessionid 发给客户端,客户端将 sessionid 存于 cookie,以后每次请求都带上 cookie,服务器根据 cookie 中的 sessionid 去查询 session,从而获取用户信息,就这样实现了身份认证
    (三)基于 token 认证方式:用户第一次登陆验证通过时服务器将用户信息等生成 token,然后返回给客户端,客户端下次访问时带上 token,服务器解析 token 就可以获取用户信息;
    token 与 session 相比:token 存储在客户端,session 存储在服务器,因此 token 适用手机 app 与服务器通信而 session 不适合
    (四) oauth2 第三方授权
    (五)签名:防篡改防重放,客户端将每一个请求参数与值即 key-value 对排序(末尾加上 appscret )排序然后生成签名然后发给给服务端,服务端验证自己生成的签名与客户端的是否相同(根据客户端发来的 appkey 查找对应的),相同即请求未被篡改,而根据时间戳就可以限制该客户端访问次数,从而避免流量丢失
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2187 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 16:08 PVG 00:08 LAX 08:08 JFK 11:08
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86