Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Go言語で学ぶYahoo! ID連携の黒帯ハンズオン / 20200204-kurobi-hands-on

kura
February 04, 2020

Go言語で学ぶYahoo! ID連携の黒帯ハンズオン / 20200204-kurobi-hands-on

ID連携の入門者向けのOpenID Connectの解説とハンズオンの資料です。

OpenID Connectはユーザーの認証と認可を連携するためのID連携の仕組みです。OAuth 2.0を拡張した仕様であり、HTTP通信やJSONなど基礎的なWeb技術によって構成されています。
ヤフーが提供するYahoo! ID連携を用いてGo言語でID連携を実装します。実装の注意点とそのリスク、仕様に施されているセキュリティ対策についてハンズオンを行いながら解説します。

ID連携に興味がある方、Go言語でOAuth 2.0やOpenID Connectをつないでみたい方などぜひ資料をご参照ください。

kura

February 04, 2020
Tweet

More Decks by kura

Other Decks in Programming

Transcript

  1. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    2020年2⽉4⽇
    ヤフー株式会社 倉林 雅
    ⿊帯ハンズオン 〜認証技術〜
    Go⾔語で学ぶYahoo! ID連携の⿊帯ハンズオン

    View Slide

  2. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    プロフィール
    2
    倉林 雅(くらはやし まさる)
    ヤフー株式会社
    認証技術⿊帯 / ID・セキュリティエンジニア
    OpenID ファウンデーション・ジャパン
    理事 / エバンジェリスト

    View Slide

  3. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    3
    https://twitter.com/kura_lab

    View Slide

  4. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    ⿊帯制度
    4
    該当分野について突出した知識とスキルを
    持っている第⼀⼈者
    ⿊帯制度
    http://hr.yahoo.co.jp/workplace/culture.html

    View Slide

  5. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    アジェンダ
    5
    1. OpenID Connectの概要説明
    2. Go⾔語によるYahoo! ID連携の実装
    3. 実装のポイント、セキュリティ対策の解説
    4. まとめ

    View Slide

  6. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OpenID Connectの概要説明

    View Slide

  7. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    7
    OpenID Connectの提供する機能
    簡単にいうと
    「ユーザー認証」と「属性情報取得」

    View Slide

  8. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    8
    所持しているIDのサービスを選択
    ログインをする

    View Slide

  9. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    9
    同意をする
    情報がプリセットされる

    View Slide

  10. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    ⽤語集
    10
    IdP (Identity Provider)
    • 認証を⾏ってID、属性情報を提供する
    RP (Relying Party)
    • IdPの認証を利⽤してEnd-Userにサービスを提供する
    Claim
    • IdPやEnd-Userなどの属性情報

    View Slide

  11. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OpenIDとは
    11
    • OpenIDはユーザーの認証の技術
    • (OpenID AXで属性を取得する拡張はある)
    • XML・SOAPベースのプロトコル

    View Slide

  12. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OpenID
    12
    2006年
    • OpenID Authentication 1.1
    • OpenID Simple Registration Extension 1.0
    2007年
    • OpenID Authentication 2.0
    • OpenID Attribute Exchange 1.0

    View Slide

  13. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OAuth
    13
    • OAuth 1.0とOAuth 2.0はユーザーの認可の技術
    • ユーザーのリソースアクセス(Web API)が⽬的
    • JSONとRESTライクのプロトコル

    View Slide

  14. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OAuth
    14
    2009年
    • OAuth 1.0a
    2010年
    • OAuth 1.0 Protocol
    2012年
    • OAuth 2.0 Authorization Framework
    オーオースッ

    View Slide

  15. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OpenID Connectとは
    15
    • ユーザーの認証と認可を兼ね備えた技術
    • OpenIDとは異なる技術でOAuth 2.0を拡張した
    プロトコル
    • JSONとRESTライクのプロトコル
    • 2014年2⽉にローンチ

    View Slide

  16. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    16
    https://openid.net/connect/

    View Slide

  17. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    17
    https://openid.net/connect/
    OpenID ConnectはOAuth 2.0や
    JSON Web Tokenなどの技術で構成されている

    View Slide

  18. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OpenID Connect
    18
    Web API連携・ユーザー認証・
    ユーザー属性情報取得に必要な仕様が定義れている
    認可 認証 属性取得
    OAuth 2.0 JSON Web Token

    View Slide

  19. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OpenID Connect
    19
    • OpenID Connectには多様なユースケースに適
    応できるよう3つのフローが定義されている
    1. Implicit Flow
    2. Authorization Code Flow
    3. Hybrid Flow

    View Slide

  20. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Implicit Flow
    20
    • 主にスクリプト⾔語を⽤いて実装されブラウザ上で
    動作するClientによって使⽤される
    • Access TokenとID Tokenは、Clientに直接返却され
    End-UserとEnd-UserのUser Agentにアクセスする
    アプリケーションに露出する可能性がある
    • Authorization Serverはクライアント認証を⾏わない

    View Slide

  21. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    21
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    Resource Server
    (UserInfo)
    0.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    6.Access Token/ID Token
    8.Access Token
    9.Claims
    4.同意画⾯
    10.属性情報取得完了
    7.ログイン完了
    Implicit Flow

    View Slide

  22. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Authorization Code Flow
    22
    • ClientにAuthorization Codeを返却し、Clientはそれを
    直接ID TokenおよびAccess Tokenと交換するため、
    User AgentやUser Agent上の他の不正アプリケーショ
    ンなどにトークンが露呈することがない
    • Authorization Serverは、Authorization Codeを
    Access Tokenと交換する前にClientを認証することも
    できる
    • Client SecretをClientとAuthorization Serverの間でセ
    キュアに保てるClientに適している

    View Slide

  23. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    23
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    Resource Server
    (UserInfo)
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    11.Access Token
    12.Claims
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    13.属性情報取得完了
    9.ログインセッション
    発⾏
    10.ログイン完了
    Authorization Code Flow

    View Slide

  24. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Hybrid Flow
    24
    • Authorization EndpointからAccess TokenとID Token
    がブラウザー上のClientに直接返却される
    • さらにAuthorization Codeが返却され、Token
    EndpointでAccess TokenとID TokenがBack-End
    Serverに返却される

    View Slide

  25. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    25
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    Resource Server
    (UserInfo)
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    12.Access Token/ID Token
    15.Access Token
    16.Claims
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code/Access Token/ID Token
    11.Tokenリクエスト(Authorization Code)
    17.属性情報取得完了
    13.ログインセッション
    発⾏
    14.ログイン完了
    10.Authorization Code
    8.Access Token
    7.クライアントサイド
    ログイン完了
    9.クライアントサイド
    属性情報取得完了
    Hybrid Flow

    View Slide

  26. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Hybrid Flowを導⼊する前に
    26
    • Authorization Codeをサーバーへ送信する前に、
    Clientサイドでユーザー認証をいち早く完了させたい場
    合などに利⽤できる
    • ただし、⼤抵の3rd PartyアプリケーションはID管理を
    Back-End Serverで⾏うため、Clientサイドのユー
    ザー認証は不要である
    • Hybrid Flowを導⼊する前に、Authorization Code
    FlowでID連携の要件を満たすことができるか検討すべ
    きである

    View Slide

  27. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Go⾔語によるYahoo! ID連携の実装

    View Slide

  28. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    0. 事前準備
    • Go実⾏環境セットアップ
    • 以下の⼿順に従い環境を設定し動作確認をしてください。
    • https://github.com/kura-lab/kuroobi-hands-on-
    2020/tree/20200204/practice00
    • 今回は「go1.13.7」のバージョンを使⽤します。
    注意点
    2020年2⽉4⽇時点の演習のソースコードは
    「master」ブランチではなく「20200204」ブランチになっています。

    View Slide

  29. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    0. 事前準備
    • OpenID Connectを提供しているYahoo! JAPANの
    Yahoo! ID連携 v2を利⽤します。
    • Yahoo! ID連携とは
    • https://developer.yahoo.co.jp/yconnect/v2/introduction.html

    View Slide

  30. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    0. 事前準備
    30
    • 以下のサイトの1〜3のステップに従いClient IDを登録する。
    • Yahoo! ID連携 v2 > 利⽤までのステップ
    • https://developer.yahoo.co.jp/yconnect/v2/
    • 今回は「サーバーサイド(Yahoo! ID連携 v2)」のClient IDを
    使って「Authorization Code Flow」をGo⾔語で実装します。
    • Client IDとClient Secretを発⾏してしてください。
    • コールバックURLに「http://localhost:8080/callback」を
    登録してください。

    View Slide

  31. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    1. Authorizationリクエスト
    31
    • RP(Client)からUserAgentを利⽤してIdP
    (AuthN/AuthZ Server)へリクエストする
    • IdPのID(Yahoo! JAPAN ID)でログインし、ユー
    ザー同意画⾯を表⽰し同意を取得する
    https://github.com/kura-lab/kuroobi-hands-on-2020/tree/20200204/practice01
    演習ソースコード

    View Slide

  32. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    32




    ログイン






    <br/>
    ログイン


    templates/index.html

    View Slide

  33. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    33




    ログイン






    <br/>
    ログイン


    templates/index.html
    テンプレートへ渡される値の
    プレースホルダー

    View Slide

  34. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    34




    ログイン完了





    <br/>
    ...
    templates/callback.html

    View Slide

  35. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    35
    ...

    ログイン完了

    ユーザー識別⼦︓{{ .Subject }}

    メールアドレス︓{{ .Email }}

    戻る

    View Slide

  36. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    36
    ...

    ログイン完了

    ユーザー識別⼦︓{{ .Subject }}

    メールアドレス︓{{ .Email }}

    戻る

    View Slide

  37. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    37




    エラー






    <br/>
    {{.}}


    templates/error.html

    View Slide

  38. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    38
    func main() {
    // 1-1. マルチプレクサにハンドラを登録
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, fmt.Sprintf("http://localhost:%d/index", config.Port), http.StatusMovedPermanently)
    })
    mux.HandleFunc("/index", index)
    mux.HandleFunc("/callback", callback)
    ...
    practice01/server.go

    View Slide

  39. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    39
    func main() {
    // 1-1. マルチプレクサにハンドラを登録
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, fmt.Sprintf("http://localhost:%d/index", config.Port), http.StatusMovedPermanently)
    })
    mux.HandleFunc("/index", index)
    mux.HandleFunc("/callback", callback)
    ...
    practice01/server.go
    アクセスのあったURLに合わせて
    ルーティング

    View Slide

  40. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    40
    ...
    // 1-2. サーバー設定
    server := &http.Server{
    Addr: fmt.Sprintf("0.0.0.0:%d", config.Port),
    Handler: mux,
    ReadTimeout: time.Second * 10,
    WriteTimeout: time.Second * 600,
    MaxHeaderBytes: 1 << 20, // 1MB
    }
    err := server.ListenAndServe()
    if err != nil {
    log.Fatal(err)
    }
    }
    practice01/server.go

    View Slide

  41. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    41
    ...
    const (
    Port = 8080
    )
    ...
    // 1-3. Client ID、Client Secretを定義
    const (
    ClientID = ""
    ClientSecret = ""
    )
    // 1-4. リダイレクトURIを定義
    var RedirectURI = fmt.Sprintf("http://localhost:%d/callback", Port)
    // 1-5. OpenID ConnectのURLを定義
    const (
    OIDCURL = "https://auth.login.yahoo.co.jp"
    )
    ...
    config/config.go

    View Slide

  42. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    42
    ...
    const (
    Port = 8080
    )
    ...
    // 1-3. Client ID、Client Secretを定義
    const (
    ClientID = ""
    ClientSecret = ""
    )
    // 1-4. リダイレクトURIを定義
    var RedirectURI = fmt.Sprintf("http://localhost:%d/callback", Port)
    // 1-5. OpenID ConnectのURLを定義
    const (
    OIDCURL = "https://auth.login.yahoo.co.jp"
    )
    ...
    config/config.go
    ⼤⽂字はじまりの変数︓Public変数
    ⼩⽂字はじまりの変数︓Private変数

    View Slide

  43. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    43
    ...
    import (
    ...
    // 1-6. 設定パッケージのインポート
    "github.com/kura-lab/kuroobi-hands-on-2020/config"
    )
    ...
    practice01/server.go

    View Slide

  44. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    44
    ...
    // 1-7. テンプレートをレンダリング
    var (
    indexTemplate = template.Must(template.ParseFiles(”../templates/index.html"))
    callbackTemplate = template.Must(template.ParseFiles("../templates/callback.html"))
    errorTemplate = template.Must(template.ParseFiles("../templates/error.html"))
    )
    ...
    practice01/server.go

    View Slide

  45. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    45
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    Resource Server
    (UserInfo)
    Authorization Code Flow

    View Slide

  46. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    46
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    0-2.処理開始
    Authorization Code Flow
    Resource Server
    (UserInfo)

    View Slide

  47. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    47
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    0-2.処理開始
    Authorization Code Flow
    RPからIdPへ認証・認可をUser Agentを利⽤して
    GETのリダイレクト(もしくはPOST)でリクエスト
    Resource Server
    (UserInfo)

    View Slide

  48. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    48
    HTTP/1.1 302 Found
    Location:
    https://auth.login.yahoo.co.jp/yconnect/v2/authorization?
    client_id=dj00aiZpPXVlM...&
    response_type=code&
    scope=openid+email&
    redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fcallback&
    state=abc&
    nonce=xyz&
    prompt=consent

    View Slide

  49. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    49
    // AuthorizationリクエストのURLを⽣成
    func index(w http.ResponseWriter, r *http.Request) {
    // 1-8. AuthorizationリクエストURL⽣成
    u, err := url.Parse(config.OIDCURL)
    if err != nil {
    // 1-9. 構造体にエラー⽂⾔を格納してerror.htmlをレンダリング
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "url parse error")
    return
    }
    u.Path = path.Join(u.Path, "yconnect/v2/authorization")
    q := u.Query()
    ...
    practice01/server.go

    View Slide

  50. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    50
    // AuthorizationリクエストのURLを⽣成
    func index(w http.ResponseWriter, r *http.Request) {
    // 1-8. AuthorizationリクエストURL⽣成
    u, err := url.Parse(config.OIDCURL)
    if err != nil {
    // 1-9. 構造体にエラー⽂⾔を格納してerror.htmlをレンダリング
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "url parse error")
    return
    }
    u.Path = path.Join(u.Path, "yconnect/v2/authorization")
    q := u.Query()
    ...
    practice01/server.go
    テンプレートの
    プレースホルダーに渡す値

    View Slide

  51. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    51
    ...
    // 1-10. response_typeにAuthorization Code Flowを指定
    q.Set("response_type", "code")
    q.Set("client_id", config.ClientID)
    q.Set("redirect_uri", config.RedirectURI)
    // 1-11. UserInfoエンドポイントから取得するscopeを指定
    q.Set("scope", "openid email")
    // 1-12. ログイン画⾯と同意画⾯の強制表⽰
    q.Set("prompt", "login consent")
    q.Set("nonce", "NONCE_STUB")
    u.RawQuery = q.Encode()
    // 1-13. 構造体にURLをセットしindex.htmlをレンダリング
    w.WriteHeader(http.StatusOK)
    indexTemplate.Execute(w, u.String())
    }
    practice01/server.go

    View Slide

  52. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    52
    ...
    // 1-10. response_typeにAuthorization Code Flowを指定
    q.Set("response_type", "code")
    q.Set("client_id", config.ClientID)
    q.Set("redirect_uri", config.RedirectURI)
    // 1-11. UserInfoエンドポイントから取得するscopeを指定
    q.Set("scope", "openid email")
    // 1-12. ログイン画⾯と同意画⾯の強制表⽰
    q.Set("prompt", "login consent")
    q.Set("nonce", "NONCE_STUB")
    u.RawQuery = q.Encode()
    // 1-13. 構造体にURLをセットしindex.htmlをレンダリング
    w.WriteHeader(http.StatusOK)
    indexTemplate.Execute(w, u.String())
    }
    practice01/server.go
    response_typeの指定によってフローがわかる
    response_typeの値 Flow
    code Authorization Code
    id_token Implicit
    id_token token Implicit
    code id_token Hybrid
    code token Hybrid
    code id_token token Hybrid

    View Slide

  53. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    53
    ...
    // 1-10. response_typeにAuthorization Code Flowを指定
    q.Set("response_type", "code")
    q.Set("client_id", config.ClientID)
    q.Set("redirect_uri", config.RedirectURI)
    // 1-11. UserInfoエンドポイントから取得するscopeを指定
    q.Set("scope", "openid email")
    // 1-12. ログイン画⾯と同意画⾯の強制表⽰
    q.Set("prompt", "login consent")
    q.Set("nonce", "NONCE_STUB")
    u.RawQuery = q.Encode()
    // 1-13. 構造体にURLをセットしindex.htmlをレンダリング
    w.WriteHeader(http.StatusOK)
    indexTemplate.Execute(w, u.String())
    }
    practice01/server.go
    ログイン画⾯(login)、同意画⾯(consent)の表⽰を強制
    しt礼しないと不要な場合は省略される

    View Slide

  54. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    54
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    4.同意画⾯
    0-2.処理開始
    Authorization Code Flow
    IdPがログイン・同意をユーザーに要求
    Resource Server
    (UserInfo)

    View Slide

  55. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    2. Tokenリクエスト
    55
    • Authorizationリクエストを正常に終えると
    Authorization Code値が返却される
    • Authorization Code値をIdP(AuthN/AuthZ Server)
    へリクエストし、Access TokenとRefresh Token、ID
    Tokenを取得する
    https://github.com/kura-lab/kuroobi-hands-on-2020/tree/20200204/practice02
    演習ソースコード

    View Slide

  56. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    56
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    Authorization Code Flow
    ユーザーが認可した情報が
    Authorization Codeとして
    User Agentを通じて
    Back-End Serverへ返却される
    Access Token等を
    直接Clientサイドへ返却せず
    有効期限の短い⼀時トークンとして
    Authorization Codeを
    返却するためImplicit Flowよりも強固
    Resource Server
    (UserInfo)

    View Slide

  57. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    57
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    Authorization Code Flow
    Authorization Codeに加えてサーバー間で
    Client IDとClient Secretによるクライアント認証を
    ⾏うためImplicit Flowよりも強固
    Resource Server
    (UserInfo)

    View Slide

  58. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    58
    POST /yconnect/v2/token HTTP/1.1
    Host: auth.login.yahoo.co.jp
    Content-Type: application/x-www-form-urlencoded
    grant_type=authorization_code&client_id=djsaWss2ka6sn...&
    client_secret=inlakw1cqa2asCcr...&
    code=SplxlOBeZQQYbYS6WxSbIA&
    redirect_uri=https%3A%2F%2Flocalhoost%3A8080%2Fcallback

    View Slide

  59. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    59
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    Authorization Code Flow
    認可のAccess Tokenと
    IdPが⾏ったユーザー認証の
    情報を含むID Tokenを返却
    Resource Server
    (UserInfo)

    View Slide

  60. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    60
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    9.ログインセッション
    発⾏
    10.ログイン完了
    Authorization Code Flow
    Resource Server
    (UserInfo)

    View Slide

  61. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    61
    HTTP/1.1 200 OK
    Cache-Control: private, no-store, no-cache, must-revalidate
    Pragma: no-cache
    Content-Type: application/json
    {
    "access_token":"KyTCfK8VtfW...",
    "token_type":"Bearer",
    "expires_in":3600,
    "id_token":"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3Mi
    OiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leG
    FtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-
    mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
    "refresh_token":"gE3tiP5nPp7...”
    }

    View Slide

  62. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    62
    // Access Tokenの取得、ID Tokenの取得と検証
    // UserInfoエンドポイントからユーザー属性情報の取得
    func callback(w http.ResponseWriter, r *http.Request) {
    // 2-1. クエリを取得
    query := r.URL.Query()
    ...
    // 2-2. Tokenリクエスト
    values := url.Values{}
    values.Set("grant_type", "authorization_code")
    values.Add("client_id", config.ClientID)
    values.Add("client_secret", config.ClientSecret)
    values.Add("redirect_uri", config.RedirectURI)
    ...
    practice02/server.go

    View Slide

  63. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    63
    // Access Tokenの取得、ID Tokenの取得と検証
    // UserInfoエンドポイントからユーザー属性情報の取得
    func callback(w http.ResponseWriter, r *http.Request) {
    // 2-1. クエリを取得
    query := r.URL.Query()
    ...
    // 2-2. Tokenリクエスト
    values := url.Values{}
    values.Set("grant_type", "authorization_code")
    values.Add("client_id", config.ClientID)
    values.Add("client_secret", config.ClientSecret)
    values.Add("redirect_uri", config.RedirectURI)
    ...
    practice02/server.go
    grant_typeでAuthorization Codeの値を渡すことを指定
    refresh_tokenを指定することでAccess Tokenの更新もできる

    View Slide

  64. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    64
    ...
    // 2-3. redirect_uriからAuthorization Codeを抽出
    values.Add("code", query["code"][0])
    tokenResponse, err := http.Post(config.OIDCURL+"/yconnect/v2/token",
    "application/x-www-form-urlencoded",
    strings.NewReader(values.Encode()))
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to post request")
    return
    }
    ...
    practice02/server.go

    View Slide

  65. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    65
    ...
    defer func() {
    _, err = io.Copy(ioutil.Discard, tokenResponse.Body)
    if err != nil {
    log.Panic(err)
    }
    err = tokenResponse.Body.Close()
    if err != nil {
    log.Panic(err)
    }
    }()
    ...
    practice02/server.go

    View Slide

  66. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    66
    ...
    defer func() {
    _, err = io.Copy(ioutil.Discard, tokenResponse.Body)
    if err != nil {
    log.Panic(err)
    }
    err = tokenResponse.Body.Close()
    if err != nil {
    log.Panic(err)
    }
    }()
    ...
    practice02/server.go
    Discardで書き込んだバイトを捨てる

    View Slide

  67. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    67
    ...
    // 2-4. TokenエンドポイントのJSONレスポンスの結果を格納する構造体
    type TokenResponse struct {
    AccessToken string `json:"access_token"`
    TokenType string `json:"token_type"`
    RefreshToken string `json:"refresh_token"`
    ExpiresIn int `json:"expires_in"`
    IDToken string `json:"id_token"`
    }
    ...
    practice02/server.go

    View Slide

  68. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    68
    ...
    // 2-5. Tokenレスポンスを構造体に格納
    var tokenData TokenResponse
    err = json.NewDecoder(tokenResponse.Body).Decode(&tokenData)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to read token's json body")
    return
    }
    log.Println("requested token endpoint")
    ...
    practice02/server.go

    View Slide

  69. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    3. UserInfoリクエスト
    69
    • Tokenリクエストで取得したAccess TokenをUserInfo
    エンドポイントへリクエストする
    • IdPに登録されているユーザーの属性情報を取得する
    https://github.com/kura-lab/kuroobi-hands-on-2020/tree/20200204/practice03
    演習ソースコード

    View Slide

  70. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    UserInfoエンドポイント
    70
    • ⽒名や住所、メールアドレスなどの属性情報を標準仕様
    化して取得しやすいように属性情報(Claim)を定義
    • 関連した属性情報ごとにアクセス制限としてScopeを定

    • RPのサービスに必要な属性情報だけをIdPに要求するこ
    とが可能

    View Slide

  71. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    定義されている属性情報
    71
    Claim Scope 説明
    sub - ユーザー識別⼦
    name profile ⽒名
    give_name profile 名
    family_name profile 姓
    middle_name profile ミドルネーム
    nickname profile ニックネーム
    preferred_
    username
    profile 簡略名
    Claim Scope 説明
    profile profile
    プロフィール情報の
    URL
    picture profile
    プロフィール画像の
    URL
    website profile サイトURL
    gender profile 性別
    birthdate profile ⽣年⽉⽇
    email email メールアドレス
    email_verified email
    メールアドレスの
    検証済みの有無

    View Slide

  72. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    定義されている属性情報
    72
    Claim Scope 説明
    zoneinfo profile タイムゾーン
    locale profile 国コード
    update profile 属性情報更新⽇時
    phone_number phone 電話番号
    phone_number
    _verified
    phone
    電話番号の検証済み
    の有無
    Claim Scope 説明
    address/country address 国コード
    address/postal_code address 郵便番号
    address/region address 都道府県
    address/locality address 市区町村
    address/formatted address 住所

    View Slide

  73. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    ユーザー属性情報取得の留意点
    73
    • 全Scopeを指定すればすべての属性情報を取得すること
    ができる
    • 関連した属性情報ごとにScopeがわけられているのは、
    サービスに必要なものに絞って取得できるにしている
    • 不要な属性情報の取得はユーザーの不安につながるため
    注意が必要

    View Slide

  74. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    74
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    11.Access Token
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    9.ログインセッション
    発⾏
    10.ログイン完了
    Authorization Code Flow
    属性情報の取得が必要な場合は
    UserInfoエンドポイントへ
    Access Tokenを送信
    Resource Server
    (UserInfo)

    View Slide

  75. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    75
    GET /yconnect/v2/attribute HTTP/1.1
    Host: auth.login.yahoo.co.jp
    Authorization: Bearer SlAV32hkKG...

    View Slide

  76. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    76
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    11.Access Token
    12.Claims
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    13.属性情報取得完了
    9.ログインセッション
    発⾏
    10.ログイン完了
    Authorization Code Flow
    各属性情報がJSONで返却される
    Resource Server
    (UserInfo)

    View Slide

  77. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    77
    HTTP/1.1 200 OK
    Content-Type: application/json
    {
    “sub”: “YIZ7F3ZBD...”,
    “name”: “倉林 雅”,
    “given_name”: “雅”,
    “family_name”: “倉林”,
    “nickname”: “kura”,
    “picture”: “https://giwiz-display-name.c.yimg.jp/d/iwiz-display-
    name/...”,
    “email”: “[email protected]
    }

    View Slide

  78. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    78
    ...
    // 3-1. UserInfoリクエスト
    userInfoRequest, err := http.NewRequest(
    "POST",
    "https://userinfo.yahooapis.jp/yconnect/v2/attribute",
    nil,
    )
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to create user attribute request")
    return
    }
    ...
    practice03/server.go

    View Slide

  79. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    79
    ...
    userInfoRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    userInfoRequest.Header.Set("Authorization", "Bearer "+tokenData.AccessToken)
    userInfoResponse, err := http.DefaultClient.Do(userInfoRequest)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to user attribute request")
    return
    }
    ...
    practice03/server.go

    View Slide

  80. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    80
    ...
    userInfoRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    userInfoRequest.Header.Set("Authorization", "Bearer "+tokenData.AccessToken)
    userInfoResponse, err := http.DefaultClient.Do(userInfoRequest)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to user attribute request")
    return
    }
    ...
    practice03/server.go
    OAuth 2.0 Bearer Token
    認可されたAccess Tokenを指定

    View Slide

  81. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    81
    ...
    defer func() {
    _, err = io.Copy(ioutil.Discard, userInfoResponse.Body)
    if err != nil {
    log.Panic(err)
    }
    err = userInfoResponse.Body.Close()
    if err != nil {
    log.Panic(err)
    }
    }()
    ...
    practice03/server.go

    View Slide

  82. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    82
    ...
    // 3-2. UserInfoエンドポイントのJSONレスポンスの結果を格納する構造体
    type UserInfoResponse struct {
    Subject string `json:"sub"`
    Email string `json:"email"`
    }
    ...
    practice03/server.go

    View Slide

  83. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    83
    ...
    // 3-3. UserInfoレスポンスを構造体に格納
    var userInfoData UserInfoResponse
    err = json.NewDecoder(userInfoResponse.Body).Decode(&userInfoData)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to parse user info json")
    return
    }
    log.Println("requested userinfo endpoint")
    ...
    // 3-4. 構造体にユーザー属性情報をセットしcallback.htmlをレンダリング
    w.WriteHeader(http.StatusOK)
    callbackTemplate.Execute(w, userInfoData)
    log.Println("[[ login completed ]]")
    ...
    practice03/server.go

    View Slide

  84. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    実装のポイント、セキュリティ対策の解説

    View Slide

  85. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    CSRF (Cross-Site Request Forgery)
    85
    ⼀般的な場合
    • 別のサイトに⽤意したコンテンツ上の罠のリンクを踏ま
    せることなどをきっかけとして、インターネットショッ
    ピングの最終決済や退会などのWebアプリケーション
    の重要な処理を呼び出すようユーザーを誘導する攻撃
    OAuth 2.0/OpenID Connectの場合
    • 悪意あるユーザーの認可コードを被害者に乗っ取らせ情
    報を窃取する

    View Slide

  86. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    86
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    9.Access Token
    10.Claims
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    11.属性情報取得
    ログイン完了
    Authorization Code Flow
    CSRFの
    攻撃対象になる部分は
    どこでしょう︖
    Resource Server
    (UserInfo)

    View Slide

  87. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    87
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    9.Access Token
    10.Claims
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    11.属性情報取得
    ログイン完了
    Authorization Code Flow
    他ユーザーのAuthorization Codeに
    置き換えができてしまう
    Resource Server
    (UserInfo)
    対策なしのままではAuthorizationリクエストから
    Authorization Codeを受け取るまで
    セッションが同⼀であることを保証できない

    View Slide

  88. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    88
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    9.Access Token
    10.Claims
    4.同意画⾯
    0-2.処理開始
    6.悪意あるユーザーのAuthorization Code
    7.Tokenリクエスト(Authorization Code)
    11.属性情報取得
    ログイン完了
    Authorization Code Flow
    悪意あるユーザーが⾃⾝のIDで
    ログイン、同意を⾏いBE Serverへ送信せず
    Authorization Codeを取得する
    Resource Server
    (UserInfo)

    View Slide

  89. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    89
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    11.Access Token/ID Token
    12.Access Token
    13.Claims
    10.Tokenリクエスト(Authorization Code)
    14.属性情報取得
    ログイン完了
    End-User
    7.redirect_uri
    8.URLクリック 9.悪意あるユーザーの
    Authorization Code
    Authorization Code Flow
    悪意あるユーザーのIDで取得した
    Access Tokenを含んだredirect_uriを
    フィッシングサイトなどでアクセスさせる
    乗っ取らせてサービスを利⽤させ
    ユーザーの情報を窃取する
    Resource Server
    (UserInfo)

    View Slide

  90. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    state
    90
    • OAuth 2.0にはCSRF対策として「state」パラメーター
    が定義されている
    • RPがAuthorizationリクエストで指定したstate値と
    Authorizationレスポンスで認可コードと同時に返却さ
    れるstate値の⼀致を検証することで認可コードの置き
    換えに夜乗っ取らせを防⽌する

    View Slide

  91. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    91
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト(state=xyz)
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token
    9.Access Token
    10.Claims
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code + state=xyz
    7.Tokenリクエスト(Authorization Code)
    11.属性情報取得
    ログイン完了
    Authorization Code Flow
    RPのセッション
    (HTTPOnlyのCookieなど)
    に紐付けたstate値を送信
    Authorization Codeと共にstate値が返却される
    セッションに紐づけたstate値と⼀致するか
    検証することでCSRFを防⽌
    Resource Server
    (UserInfo)

    View Slide

  92. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    92
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    11.Access Token/ID Token
    12.Access Token
    13.Claims
    10.Tokenリクエスト(Authorization Code)
    14.属性情報取得
    ログイン完了
    End-User
    7.redirect_uri
    8.URLクリック
    9.悪意あるユーザーの
    Authorization Code
    +
    state=abc
    Authorization Code Flow
    被害者のセッションに紐づくstate値
    (サービスのAuthorizationリクエストに
    アクセスしていなければセッションもない)
    と⼀致しないため置き換えを検知できる
    Resource Server
    (UserInfo)

    View Slide

  93. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    4. CSRF対策(state値の検証)
    93
    • Authorizationレスポンスで返却されるAuthorization
    Code値の置き換えによるCSRF攻撃を対策する
    • RPで⽣成したstate値をセッションCookieに紐付ける
    • state値をAuthorizationリクエストで送信する
    • 返却されたstate値がとセッションCookieに紐付けた
    state値と同⼀であるかを検証する
    https://github.com/kura-lab/kuroobi-hands-on-2020/tree/20200204/practice04
    演習ソースコード

    View Slide

  94. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    94
    ...
    // 4-1. ランダム⽂字列を⽣成
    func init() {
    rand.Seed(time.Now().UnixNano())
    }
    var randLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    func generateRandomString() string {
    result := make([]rune, 32)
    for i := range result {
    result[i] = randLetters[rand.Intn(len(randLetters))]
    }
    return string(result)
    }
    ...
    practice04/server.go

    View Slide

  95. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    95
    ...
    // 4-2. セッションCookieに紐付けるstate値を⽣成し保存
    state := generateRandomString()
    stateCookie := &http.Cookie{
    Name: "state",
    Value: state,
    HttpOnly: true,
    }
    http.SetCookie(w, stateCookie)
    ...
    // 4-3. セッションCookieに紐づけたstate値を指定
    q.Set("state", state)
    ...
    practice04/server.go

    View Slide

  96. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    96
    ...
    // 4-2. セッションCookieに紐付けるstate値を⽣成し保存
    state := generateRandomString()
    stateCookie := &http.Cookie{
    Name: "state",
    Value: state,
    HttpOnly: true,
    }
    http.SetCookie(w, stateCookie)
    ...
    // 4-3. セッションCookieに紐づけたstate値を指定
    q.Set("state", state)
    ...
    practice04/server.go
    今回はstateというフィールド名で
    Cookieを発⾏

    View Slide

  97. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    97
    ...
    // 4-4. redirect_uriからstate値の抽出
    stateQuery, ok := query["state"]
    if !ok {
    w.WriteHeader(http.StatusBadRequest)
    errorTemplate.Execute(w, "state query not found")
    return
    }
    state := stateQuery[0]
    storedState, err := r.Cookie("state")
    if err != nil {
    w.WriteHeader(http.StatusBadRequest)
    errorTemplate.Execute(w, "state cookie error")
    return
    }
    ...
    practice04/server.go

    View Slide

  98. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    98
    ...
    // 4-5. セッションCookieに紐づけていたstate値の破棄
    stateCookie := &http.Cookie{
    Name: "state",
    MaxAge: -1,
    }
    http.SetCookie(w, stateCookie)
    // 4-6. state値の検証
    if state != storedState.Value {
    w.WriteHeader(http.StatusBadRequest)
    errorTemplate.Execute(w, "state does not match stored one")
    return
    }
    log.Println("success to verify state")
    ...
    practice04/server.go

    View Slide

  99. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    ID Tokenとは
    99
    • ユーザー認証情報を含む改ざん検知⽤の署名付きToken
    • JSON Web Token(JWT)フォーマット
    • IdPが認証したユーザーの認証情報を含めRPが検証し
    RP側のセッション管理に⽤いる
    ID

    View Slide

  100. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    JSON Web Token(JWT)
    100
    • JSONをBase64urlエンコード(URL SafeなBase64エン
    コード)したシグネチャ(ハッシュ値もしくはデジタル署
    名)付きトークン
    • ヘッダー・ペイロード・シグネチャの3つから構成される
    • シグネチャはハッシュ(HMAC)と
    公開鍵暗号(RSA・ECDSA)をサポート
    • JWTと表記して「jot(ジョット)」と発⾳する Jot down

    View Slide

  101. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    101
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU
    zI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleH
    AiOjEzMDA4MTkzODAsDQogImh0dHA
    6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp
    0cnVlfQ.dBjftJeZ4CVP-
    mB92K27uhbUJU1p1r_wW1gFWFOEj
    Xk
    ID Token

    View Slide

  102. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    102
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU
    zI1NiJ9
    .
    eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzM
    DA4MTkzODAsDQogImh0dHA6Ly9leG
    FtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
    .
    dBjftJeZ4CVP-
    mB92K27uhbUJU1p1r_wW1gFWFOEj
    Xk
    Header
    Payload
    Signature
    ピリオド区切りの3つの部位から構成される

    View Slide

  103. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    103
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU
    zI1NiJ9
    .
    eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzM
    DA4MTkzODAsDQogImh0dHA6Ly9leG
    FtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
    .
    dBjftJeZ4CVP-
    mB92K27uhbUJU1p1r_wW1gFWFOEj
    Xk
    Header
    Payload
    Signature
    Base64エンコードは「/」や「=」が
    含まれURL Safeでないため
    Base64urlエンコードが利⽤されている

    View Slide

  104. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    104
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU
    zI1NiJ9
    {
    "type": "JWT",
    "alg": "RS256"
    }
    Header
    Signature
    Base64urlデコード
    置換前 置換後
    “-” “+”
    “/” “_”
    (データ⻑ % 4)の
    数だけ”=”をパディング
    Base64urlデコード
    Base64デコード

    View Slide

  105. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    105
    {
    "type": "JWT",
    "alg": "RS256"
    }
    type: JSON Web Token

    View Slide

  106. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    106
    {
    "type": "JWT",
    "alg": "RS256"
    }
    algorithm: Signatureのアルゴリズム
    RS256=RSA SHA-256
    alg アルゴリズム 実装要求
    HS256 HMAC SHA-256
    RS256 RSA SHA-256 推奨
    ES256 ECDSA SHA-256 推奨
    JWT サポートアルゴリズム

    View Slide

  107. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    107
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU
    zI1NiJ9
    .
    eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzM
    DA4MTkzODAsDQogImh0dHA6Ly9leG
    FtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
    .
    dBjftJeZ4CVP-
    mB92K27uhbUJU1p1r_wW1gFWFOEj
    Xk
    Header
    Payload
    Signature
    Header + ”.” + Payloadを
    ⼊⼒データとする

    View Slide

  108. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    108
    eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU
    zI1NiJ9
    .
    eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzM
    DA4MTkzODAsDQogImh0dHA6Ly9leG
    FtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
    .
    dBjftJeZ4CVP-
    mB92K27uhbUJU1p1r_wW1gFWFOEj
    Xk
    Header
    Payload
    Signature
    検証結果が正しければ
    PayloadのClaim(属性情報)を参照する
    typのアルゴリズムで⼊⼒データとSignatureと
    IdPが公開している公開鍵をつかって改ざんを検証

    View Slide

  109. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    109
    eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzM
    DA4MTkzODAsDQogImh0dHA6Ly9leG
    FtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
    {
    "iss":"https://idp.example.com",
    "sub":"123456789",
    "aud":"abcdefg",
    "nonce":"xyz",
    "iat":1291836800,
    "exp":1300819380,
    "nonce":"xyz..."
    }
    Payload
    Base64urlデコード

    View Slide

  110. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    110
    {
    "iss":"https://idp.example.com",
    "sub":"123456789",
    "aud":"abcdefg",
    "nonce":"xyz",
    "iat":1291836800,
    "exp":1300819380,
    "nonce":"xyz..."
    }
    issuer
    ID Tokenの発⾏社(IdP)

    View Slide

  111. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    111
    {
    "iss":"https://idp.example.com",
    "sub":"123456789",
    "aud":"abcdefg",
    "nonce":"xyz",
    "iat":1291836800,
    "exp":1300819380,
    "nonce":"xyz..."
    }
    subject
    ユーザー識別⼦(認証の対象者)

    View Slide

  112. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    112
    {
    "iss":"https://idp.example.com",
    "sub":"123456789",
    "aud":"abcdefg",
    "nonce":"xyz",
    "iat":1291836800,
    "exp":1300819380,
    "nonce":"xyz..."
    }
    audience
    Client ID(ID Tokenの発⾏先)

    View Slide

  113. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    113
    {
    "iss":"https://idp.example.com",
    "sub":"123456789",
    "aud":"abcdefg",
    "nonce":"xyz",
    "iat":1291836800,
    "exp":1300819380,
    "nonce":"xyz..."
    }
    発⾏社(iss)が
    どのRP(aud)に対して
    どのユーザー(sub)を認証した
    のかを⽰している

    View Slide

  114. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    リプレイ攻撃
    114
    ⼀般的な場合
    • 有効なデータ転送が故意または不正に繰り返し/遅延される
    ことによる攻撃
    • IPパケットの置換によるDNS偽装の⼀部のように、発信者
    や攻撃者がデータを傍受し再送信することによって実⾏さ
    れる
    OpenID Connectの場合
    • ID Tokenを傍受し不正ログインを試みる

    View Slide

  115. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    115
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    Resource Server
    (UserInfo)
    0.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    6.Access Token/ID Token
    8.Access Token
    9.Claims
    4.同意画⾯
    10.属性情報取得完了
    7.ログイン完了
    Implicit Flow
    リプレイ攻撃の
    対象になる部分は
    どこでしょう︖

    View Slide

  116. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    116
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    Resource Server
    (UserInfo)
    0.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    6.Access Token/ID Token
    8.Access Token
    9.Claims
    4.同意画⾯
    10.属性情報取得完了
    7.ログイン完了
    Implicit Flow
    通信経路から傍受されたID Tokenを
    受け⼊れてしまう

    View Slide

  117. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    117
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    Resource Server
    (UserInfo)
    0.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    6.Access Token/ID Token
    4.同意画⾯
    Proxyなどを⽤いて
    ID Tokenを傍受
    8.ログイン完了
    7.ID Token
    Implicit Flow

    View Slide

  118. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    nonce (number used once)
    118
    • OpenID ConnectにはID Tokenのリプレイ攻撃対策と
    して「nonce」パラメーターが定義されている
    • RPがAuthorizationリクエストで指定したnonce値と
    返却されるID Tokenの内部に含まれるnonce値の
    ⼀致を検証することで繰り返し送信されるID Tokenの
    リプレイ攻撃を防⽌する

    View Slide

  119. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    119
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0.処理開始
    1.Authorizationリクエスト(nonce=xyz)
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token
    9.Claims
    4.同意画⾯
    10.属性情報取得完了
    7.ログイン完了
    6.Access Token/ID Token(nonce=xyz)
    Implicit Flow
    RPのセッション
    (HTTPOnlyのCookieなど)
    に紐付けた値を送信
    nonce値を含んだID Tokenが返却される
    セッションに紐づけたnonce値と⼀致するか
    検証することでリプレイ攻撃を防⽌
    Resource Server
    (UserInfo)

    View Slide

  120. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    120
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0.処理開始
    1.Authorizationリクエスト
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    6.Access Token/ID Token(nonce=123)
    4.同意画⾯
    8.ログイン完了
    7.ID Token(nonce=123)
    Implicit Flow
    被害者のセッションに紐づくnonce値
    (サービスのAuthorizationリクエストに
    アクセスしていなければセッションもない)
    と⼀致しないため置き換えを検知できる
    Resource Server
    (UserInfo)

    View Slide

  121. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    nonceの検証
    121
    • nonceの検証はID Tokenの再送が起こりうるケースのみ必須
    • クライアントサイドでID Tokenを受け取るHybridフローの場合は
    必須
    • Authorization CodeフローのRPとIdPのサーバー間で安全に
    ID Tokenが送受信される場合にはnonceの検証は任意でよい

    View Slide

  122. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    5. リプレイ攻撃対策(nonce値の検証)
    122
    • Tokenレスポンスで返却されるID Tokenの再送による
    リプレイ攻撃を対策する
    • RPで⽣成したnonce値をセッションCookieに紐付ける
    • nonce値をAuthorizationリクエストで送信する
    • ID Tokenに含まれるnonce値がセッションCookieに紐
    付けたnonce値と同⼀であるかを検証する
    https://github.com/kura-lab/kuroobi-hands-on-2020/tree/20200204/practice05
    演習ソースコード

    View Slide

  123. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    123
    End-User
    Client
    (User Agent)
    Back-End
    Server
    AuthN/AuthZ
    Server
    0-1.処理開始
    1.Authorizationリクエスト(nonce=xyz)
    2.ログイン画⾯
    3.クレデンシャル
    情報⼊⼒
    5.同意
    8.Access Token/ID Token(nonce=xyz)
    4.同意画⾯
    0-2.処理開始
    6.Authorization Code
    7.Tokenリクエスト(Authorization Code)
    9.ログインセッション
    発⾏
    10.ログイン完了
    Authorization Code Flow
    ID Tokenに含まれる
    nonce値を検証
    nonce値を⽣成し
    セッションCookieに紐づける
    Resource Server
    (UserInfo)

    View Slide

  124. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    124
    ...
    // 5-1. セッションCookieに紐付けるnonce値を⽣成し保存
    nonce := generateRandomString()
    nonceCookie := &http.Cookie{
    Name: "nonce",
    Value: nonce,
    HttpOnly: true,
    }
    http.SetCookie(w, nonceCookie)
    log.Println("stored state and nonce in session")
    ...
    // 5-2. セッションCookieに紐づけたnonce値を指定
    q.Set("nonce", nonce)
    ...
    practice05/server.go

    View Slide

  125. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    125
    ...
    // 5-3. ID Tokenのデータ部の分解
    idTokenParts := strings.SplitN(tokenData.IDToken, ".", 3)
    log.Println("header: ", idTokenParts[0])
    log.Println("payload: ", idTokenParts[1])
    log.Println("signature: ", idTokenParts[2])
    // 5-4. ID Tokenのヘッダーの検証
    header, err := base64.RawURLEncoding.DecodeString(idTokenParts[0])
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to decode ID Token")
    return
    }
    ...
    practice05/server.go

    View Slide

  126. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    126
    ...
    // 5-3. ID Tokenのデータ部の分解
    idTokenParts := strings.SplitN(tokenData.IDToken, ".", 3)
    log.Println("header: ", idTokenParts[0])
    log.Println("payload: ", idTokenParts[1])
    log.Println("signature: ", idTokenParts[2])
    // 5-4. ID Tokenのヘッダーの検証
    header, err := base64.RawURLEncoding.DecodeString(idTokenParts[0])
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to decode ID Token")
    return
    }
    ...
    practice05/server.go
    Base64デコードではなく
    Base64URLデコード

    View Slide

  127. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    127
    ...
    // 5-5. ID Tokenのヘッダーを格納する構造体
    type IDTokenHeader struct {
    Type string `json:"typ"`
    Algorithm string `json:"alg"`
    KeyID string `json:"kid"`
    }
    ...
    // 5-6. ID Tokenのヘッダーを構造体に格納
    var idTokenHeader IDTokenHeader
    err = json.Unmarshal(header, &idTokenHeader)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to decode ID Token")
    return
    }
    ...
    practice05/server.go

    View Slide

  128. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    128
    ...
    // 5-7. typ値の検証
    if idTokenHeader.Type != "JWT" {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "invalid id token type")
    return
    }
    // 5-8. alg値の検証
    if idTokenHeader.Algorithm != "RS256" {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "invalid id token algorithm")
    return
    }
    ...
    practice05/server.go

    View Slide

  129. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    129
    ...
    // 5-9. JWKsリクエスト
    jwksResponse, err := http.Get(config.OIDCURL + "/yconnect/v2/jwks")
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to get jwk")
    return
    }
    ...
    practice05/server.go

    View Slide

  130. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    130
    ...
    defer func() {
    _, err = io.Copy(ioutil.Discard, jwksResponse.Body)
    if err != nil {
    log.Panic(err)
    }
    err = jwksResponse.Body.Close()
    if err != nil {
    log.Panic(err)
    }
    }()
    ...
    practice05/server.go

    View Slide

  131. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    131
    {
    "keys": [
    {
    "kid": "0cc175b9c0f1b6a831c399e269772661",
    "kty": "RSA",
    "alg": "RS256",
    "use": "sig",
    "n": "0bXcnrheJ2snfq1wv6Qz8...",
    "e": "AQAB"
    },
    ...
    ]
    }

    View Slide

  132. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    132
    ...
    // 5-11. JWKsレスポンスを構造体に格納
    var jwksData JWKsResponse
    err = json.NewDecoder(jwksResponse.Body).Decode(&jwksData)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to read jwk's json body")
    return
    }
    log.Println("requested jwks endpoint")
    ...
    practice05/server.go

    View Slide

  133. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    133
    ...
    // 5-12. modulus値とexponent値の抽出
    var modulus, exponent string
    for _, keySet := range jwksData.KeySets {
    if keySet.KeyID == idTokenHeader.KeyID {
    log.Println("kid: " + keySet.KeyID)
    if keySet.KeyType != "RSA" || keySet.Algorithm != idTokenHeader.Algorithm || keySet.Use != "sig" {
    w.WriteHeader(http.StatusUnauthorized)
    errorTemplate.Execute(w, "invalid kid, alg or use")
    return
    }
    modulus = keySet.Modulus
    exponent = keySet.Exponent
    break
    }
    }
    ...
    practice05/server.go

    View Slide

  134. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    134
    ...
    if modulus == "" || exponent == "" {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to extract modulus or exponent")
    return
    }
    log.Println("extracted modulus and exponent")
    ...
    practice05/server.go

    View Slide

  135. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    135
    ...
    // 5-13. n(modulus)とe(exponent)から公開鍵を⽣成
    decodedModulus, err := base64.RawURLEncoding.DecodeString(modulus)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to decode modulus")
    return
    }
    decodedExponent, err := base64.StdEncoding.DecodeString(exponent)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to decode exponent")
    return
    }
    ...
    practice05/server.go

    View Slide

  136. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    136
    ...
    var exponentBytes []byte
    if len(decodedExponent) < 8 {
    exponentBytes = make([]byte, 8-len(decodedExponent), 8)
    exponentBytes = append(exponentBytes, decodedExponent...)
    } else {
    exponentBytes = decodedExponent
    }
    ...
    practice05/server.go

    View Slide

  137. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    137
    ...
    reader := bytes.NewReader(exponentBytes)
    var e uint64
    err = binary.Read(reader, binary.BigEndian, &e)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to read binary exponent")
    return
    }
    generatedPublicKey := rsa.PublicKey{N: big.NewInt(0).SetBytes(decodedModulus), E: int(e)}
    log.Println("generated public key: ", generatedPubli
    ...
    practice05/server.go

    View Slide

  138. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    138
    ...
    // 5-14. ID Tokenの署名を検証
    decodedSignature, err := base64.RawURLEncoding.DecodeString(idTokenParts[2])
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to decode signature")
    return
    }
    hash := crypto.Hash.New(crypto.SHA256)
    _, err = hash.Write([]byte(idTokenParts[0] + "." + idTokenParts[1]))
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to create id token hash")
    return
    }
    ...
    practice05/server.go

    View Slide

  139. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    139
    ...
    hashed := hash.Sum(nil)
    err = rsa.VerifyPKCS1v15(&generatedPublicKey, crypto.SHA256, hashed, decodedSignature)
    if err != nil {
    log.Println("failed to verify signature")
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to verify signature")
    return
    }
    log.Println("success to verify signature")
    ...
    practice05/server.go

    View Slide

  140. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    140
    ...
    // 5-15. ID Tokenのペイロードをデコード
    decodedPayload, err := base64.RawURLEncoding.DecodeString(idTokenParts[1])
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to decode payload")
    return
    }
    ...
    practice05/server.go

    View Slide

  141. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    141
    ...
    // 5-16. ID Tokenのペイロードを格納する構造体
    type IDTokenPayload struct {
    Issuer string `json:"iss"`
    Subject string `json:"sub"`
    Audience []string `json:"aud"`
    Expiration int `json:"exp"`
    IssueAt int `json:"iat"`
    AuthTime int `json:"auth_time"`
    Nonce string `json:"nonce"`
    AuthenticationMethodReference []string `json:"amr"`
    AccessTokenHash string `json:"at_hash"`
    }
    ...
    practice05/server.go

    View Slide

  142. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    142
    ...
    // 5-17. ID Tokenのペイロードを構造体へ格納
    idTokenPayload := new(IDTokenPayload)
    err = json.Unmarshal(decodedPayload, idTokenPayload)
    if err != nil {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "failed to parse payload json")
    return
    }
    ...
    practice05/server.go

    View Slide

  143. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    143
    ...
    // 5-18. issuer値の検証
    log.Println("id token issuer: ", idTokenPayload.Issuer)
    if idTokenPayload.Issuer != oidcURL+"/yconnect/v2" {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "mismatched issuer")
    return
    }
    log.Println("success to verify issuer")
    ...
    practice05/server.go

    View Slide

  144. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    144
    ...
    // 5-19. audience値の検証
    log.Println("id token audience: ", idTokenPayload.Audience)
    var isValidAudience bool
    for _, audience := range idTokenPayload.Audience {
    if audience == ClientID {
    log.Println("mached audience: ", audience)
    isValidAudience = true
    break
    }
    }
    if !isValidAudience {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "mismatched audience")
    return
    }
    ...
    practice05/server.go

    View Slide

  145. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    145
    ...
    // 5-20. セッションCookieからnonce値の抽出
    storedNonce, err := r.Cookie("nonce")
    if err != nil {
    w.WriteHeader(http.StatusBadRequest)
    errorTemplate.Execute(w, "nonce cookie error")
    return
    }
    ...
    practice05/server.go

    View Slide

  146. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    146
    ...
    // 5-21. セッションCookieに紐づけていたnonce値の破棄
    nonceCookie := &http.Cookie{
    Name: "nonce",
    MaxAge: -1,
    }
    http.SetCookie(w, nonceCookie)
    log.Println("id token nonce: ", idTokenPayload.Nonce)
    log.Println("stored nonce: ", storedNonce.Value)
    if idTokenPayload.Nonce != storedNonce.Value {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "nonce does not match stored one")
    return
    }
    log.Println("success to verify nonce")
    ...
    practice05/server.go

    View Slide

  147. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    147
    ...
    // 5-22. iat値の検証
    log.Println("id token iat: ", idTokenPayload.IssueAt)
    if int(time.Now().Unix())-idTokenPayload.IssueAt >= 600 {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "too far away from current time")
    return
    }
    log.Println("success to verify issue at")
    ...
    practice05/server.go

    View Slide

  148. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    148
    ...
    // 5-23. at_hash値の検証
    receivedAccessTokenHash := sha256.Sum256([]byte(tokenData.AccessToken))
    halfOfAccessTokenHash := receivedAccessTokenHash[:len(receivedAccessTokenHash)/2]
    encodedhalfOfAccessTokenHash := base64.RawURLEncoding.EncodeToString(halfOfAccessTokenHash)
    log.Println("id token at_hash: ", idTokenPayload.AccessTokenHash)
    log.Println("generated at_hash: ", encodedhalfOfAccessTokenHash)
    if idTokenPayload.AccessTokenHash != encodedhalfOfAccessTokenHash {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "mismatched at_hash")
    return
    }
    log.Println("success to verify at_hash")
    ...
    practice05/server.go

    View Slide

  149. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    149
    ...
    // 5-23. at_hash値の検証
    receivedAccessTokenHash := sha256.Sum256([]byte(tokenData.AccessToken))
    halfOfAccessTokenHash := receivedAccessTokenHash[:len(receivedAccessTokenHash)/2]
    encodedhalfOfAccessTokenHash := base64.RawURLEncoding.EncodeToString(halfOfAccessTokenHash)
    log.Println("id token at_hash: ", idTokenPayload.AccessTokenHash)
    log.Println("generated at_hash: ", encodedhalfOfAccessTokenHash)
    if idTokenPayload.AccessTokenHash != encodedhalfOfAccessTokenHash {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "mismatched at_hash")
    return
    }
    log.Println("success to verify at_hash")
    ...
    practice05/server.go
    Access Tokenのハッシュ値の
    オクテットの前半をBase64URLエンコードした値

    View Slide

  150. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    150
    ...
    // 5-24. 以下の値の検証および利⽤は任意
    // - idTokenPayload.Expiration
    // - idTokenPayload.AuthTime
    // - idTokenPayload.AuthenticationMethodReference
    ...
    practice05/server.go

    View Slide

  151. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    151
    ...
    // 5-25. sub値の検証
    log.Println("id token sub: ", idTokenPayload.Subject)
    log.Println("userinfo sub: ", userInfoData.Subject)
    if idTokenPayload.Subject != userInfoData.Subject {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "mismatched user id")
    return
    }
    log.Println("success to verify user id")
    ...
    practice05/server.go

    View Slide

  152. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    152
    ...
    // 5-25. sub値の検証
    log.Println("id token sub: ", idTokenPayload.Subject)
    log.Println("userinfo sub: ", userInfoData.Subject)
    if idTokenPayload.Subject != userInfoData.Subject {
    w.WriteHeader(http.StatusInternalServerError)
    errorTemplate.Execute(w, "mismatched user id")
    return
    }
    log.Println("success to verify user id")
    ...
    practice05/server.go
    ID Tokenで認証したユーザーと
    UserInfoで取得したユーザーが⼀致しているかを検証

    View Slide

  153. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    まとめ

    View Slide

  154. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    まとめ
    154
    1. OpenID Connectの概要説明
    • OpenID ConnectはOpenIDとは異なりOAuth 2.0をベースとした認証・認
    可のプロトコルである
    • 多様なユースケースに適応できるよう3つのフローが定義されている
    2. Go⾔語によるYahoo! ID連携の実装
    • Authorizationリクエスト、Tokenリクエスト、UserInfoリクエストをGo
    ⾔語で実装し⼀連のAuthorization Code Flowを学んだ
    3. 実装のポイント、セキュリティ対策の解説
    • OpenID ConnectによるCSRF対策、リプレイ攻撃対策を学んだ
    • RPのユーザー認証に⽤いるID Tokenの検証を実装した

    View Slide

  155. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    ご清聴ありがとうございました

    View Slide

  156. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    Appendix

    View Slide

  157. Copyright (C) 2020 Yahoo Japan Corporation. All Rights Reserved.
    OpenID Connectの仕様について
    158
    • OpenID Connectの概要や詳細な仕様について学習をする際には、
    以下のWebサイトや仕様書、解説のスライドをご参照ください。
    • Welcome to OpenID Connect -openid.net-
    • https://openid.net/connect/
    • OpenID Connect Core 1.0 incorporating errata set 1
    • https://openid.net/specs/openid-connect-core-1_0.html
    • OpenID Connect Core 1.0 incorporating errata set 1(⽇本語訳)
    • https://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html
    • OpenID Connect ⼊⾨ 〜コンシューマーにおけるID連携のトレンド〜
    • https://www.slideshare.net/kura_lab/openid-connect-id

    View Slide