Slide 1

Slide 1 text

Server-side Kotlin in LINE Messaging API LINE Platform Development Center 1 / Bot Platform Development 室 / B Part チーム Hirotaka Kawata 2022.05.25

Slide 2

Slide 2 text

ࣗݾ঺հ ઒ా ༟و )JSPUBLB ,BXBUB !ILUFDIOP • ೥ ։ൃΠϯλʔϯγοϓ • -*/&)#BTFετϨʔδͷ؅ཧπʔϧ • ೥ ৽ଔೖࣾ -*/&4IPQνʔϜ • -*/&ελϯϓɾֆจࣈɾண͔ͤ͑ͷαʔόʔ։ൃ • ೥ -*/&5IJOHTͷ։ൃʹࢀՃ • #MVFUPPUI-&Λ࢖ͬͨ *P5αʔϏε޲͚ "1* • αʔόʔɾϋʔυ΢ΣΞɾγεςϜઃܭ౳৭ʑ • ೥ dݱࡏ -*/&.FTTBHJOH"1*αʔόʔ։ൃ • .FTTBHJOH"1*ͷαʔόʔαΠυ։ൃ • 8FCIPPLૹ৴ίϯϙʔωϯτͷஔ׵͑ • झຯं͍͡Γɾ௿ϨΠϠʔɾ$16ΞʔΩςΫνϟ

Slide 3

Slide 3 text

-*/&.FTTBHJOH"1* ͷ಺෦ -*/&ެࣜΞΧ΢ϯτΛࢧ͑Δ "1* Ұ೔ԯҎ্ͷϝοηʔδΛॲཧ͢Δ "1*όοΫΤϯυγεςϜ

Slide 4

Slide 4 text

-*/&.FTTBHJOH"1* -*/&ެࣜΞΧ΢ϯτ #PU Λ࡞ΔͨΊͷ "1*ɾϓϥοτϑΥʔϜ IUUQTEFWFMPQFSTMJOFCJ[KBTFSWJDFT NFTTBHJOHBQJ

Slide 5

Slide 5 text

.FTTBHJOH"1* #PUˠ 6TFSҰ੪഑৴ ϒϩʔυΩϟετɾφϩʔΩϟετ #PUˠ 6TFSݸผ഑৴ ϓογϡɾϦϓϥΠ 6TFSˠ #PU 8FCIPPLૹ৴ ͦͷଞ ϦονϝχϡʔɾϢʔβʔ৘ใ "1* ϝοηʔδૹ৴਺ूܭ ౳ʑʜ -*/&.FTTBHJOH"1*͕ఏڙ͢Δ "1*

Slide 6

Slide 6 text

.FTTBHJOH"1*ͷόοΫΤϯυ talk-server Messaging API Webhook Messaging API Push, Reply … https://api.line.me/v2/bot MJOFCPUCBDLFOE Chatbot server -*/&ͷ UBMLTFSWFSͱ .FTTBHJOH"1* CPU ͷڮ౉͠໾ ϝοηʔδड৴ ༑ͩͪ௥Ճ ϝοηʔδૹ৴

Slide 7

Slide 7 text

8FCIPPL&OEQPJOU #PUαʔόʔ 8FCIPPL ૹ৴αʔόʔ #PUΠϕϯτ ϑΟϧλʔ ඵؒສΠϕϯτҎ্ #PUʹؔ͢ΔΠϕϯτΛநग़ #PU8FCIPPLૹ৴ଆ αʔόʔߏ੒ Πϕϯτॲཧ αʔόʔ $3. νϟοτ ετϨʔδ UBMLTFSWFS Ϣʔβʔ #PUνϟϯωϧ ؅ཧ "1* άϧʔϓ؅ཧ ඵؒສΠϕϯτҎ্ Πϕϯτͷॱংɾ੔߹ੑΛอূ 8FCIPPLૹ৴ઌ͕ͲΜͳʹ஗ͯ͘΋ ӨڹΛड͚ͣʹ҆ఆՔಇɾ࠶ૹػೳ ೥ʹେ෯࡮৽ ,PUMJOΛੵۃ࠾༻ 8FCIPPL $PSPVUJOFT

Slide 8

Slide 8 text

UBMLTFSWFS "1*(BUFXBZ ϦΫΤετ ඵؒສҎ্ #PUϝοηʔδ഑৴ଆ αʔόʔߏ੒ ഑৴ॲཧαʔόʔ #PUνϟϯωϧ ؅ཧ "1* ೔ԯҎ্ͷϝοηʔδॲཧ ਺ઍສ୯ҐͷϢʔβʔ΁Ұ੪഑৴ ༏ઌ౓෇͖ͷ഑৴Ωϡʔ $3. νϟοτ ετϨʔδ ૹ৴਺؅ཧ ϚΠΫϩαʔϏεΞʔΩςΫνϟ ͦΕͦΕ͕ଟ਺ͷϦΫΤετΛॲཧ Ϣʔβʔ ഑৴ 2VFVF ϝοηʔδૹ৴ αʔόʔ

Slide 9

Slide 9 text

UBMLTFSWFS "1*(BUFXBZ #PUϝοηʔδ഑৴ଆ αʔόʔߏ੒ ഑৴ॲཧαʔόʔ #PUνϟϯωϧ ؅ཧ "1* $3. νϟοτ ετϨʔδ ૹ৴਺؅ཧ Ϣʔβʔ ഑৴ 2VFVF ,PUMJO ΁ͷҠߦΛܭըத ,BGLB %FDBUPO 3FBDUPS ϝοηʔδૹ৴ αʔόʔ

Slide 10

Slide 10 text

line-bot-backend (monorepo) 3FEJT 0CKFDU 4UPSBHF .POJUPSJOH 6UJMJUJFT ,BGLB %FDBUPO "1*DMJFOUT $BDIF .JDSPTFSWJDFT.POPSFQP .FTTBHJOH"1*CBDLFOEͷαʔϏεߏ੒ "1* (BUFXBZ .FTTBHF "1*4FSWFS 2VFVF 1SPDFTTPS 8FCIPPL 1SPDFTTPS 8FCIPPL %JTQBUDIFS νʔϜ಺ڞ௨ϥΠϒϥϦ DPNNPOT TUPSBHF NPEFMT CVJMEHSBEMFLUT 4QPUMFTTLUMJOU

Slide 11

Slide 11 text

+BWBͱ ,PUMJOͱ 4DBMB +BWBͱͷ ਌࿨ੑ ݴޠͷػೳ ֶशίετ +BWBҎ߱ਐԽ ·ͩ·ͩ͜Ε͔Β ࣾ಺ڞ௨ݴޠ ৑௕͕ͩ೉͕͠͞গͳ͍ +BWB͔Βؾܰʹݺ΂Δ ΄ͱΜͲࠔΒͳ͍ ҉໧తͳܕม׵ /VMMBCMF %BUBDMBTT XIFOCMPDL $PSPVUJOFT +BWBΛ࢖ͬͨ͜ͱ͕ ͋Ε͹௚ײత Kotlin Java +BWB͔Β DBMM͕ݫ͍͠ ಛ༗ͷίϨΫγϣϯܕ BT4DBMB BT+BWB ಛ༗ͳ 0QUJPO ڧྗͳ 'VODUJPOBM 1SPHSBNNJOHαϙʔτ ͱ͖ͬͭʹ͍͘ߏจ ҉໧తɺॳݟͰಡΊͳ͍ *NQMJDJU Scala ஸ౓͍͍

Slide 12

Slide 12 text

ඇಉظॲཧͷ ॻ͖΍͢͞ ಋೖͷखܰ͞ ετϦʔϜॲཧ 3FBDUJWF4USFBNT ͷֶशίετ͕ߴ͍ ରԠϥΠϒϥϦ͕ଟ͍ 4QSJOHͷαϙʔτ ετϦʔϜॲཧʹಛԽ ॆ࣮ͨ͠ 0QFSBUPS ௚ײత ݴޠػೳͷҰ෦ TVTQFOEGVO BXBJU +BWB'VUVSF΍ 3Yͱͷ ίωΫλʔ͕ॆ࣮ 'MPXͷ 0QFSBUPS͕ශऑ ୯ൃ࣮ߦͷඇಉظԽ޲͚ ඇಉظॲཧ $PSPVUJOFT3FBDUPS Coroutines Reactor

Slide 13

Slide 13 text

ྑ͍ͱ͜Ζ • +7.Ͱ͸ࠔΔ͜ͱ͕ຆͲͳ͍ • +BWBͱͷ਌࿨ੑ͕࠷ߴ • 4DBMBͱൺ΂ͯ΋֨ஈʹྑ͍ • ͔Ώ͍ͱ͜Ζʹख͕ಧ͘ • /VMMBCMF TNBSUDBTU • ֦ுؔ਺͕Α͍ • ಛʹ *%&ͱͷ਌࿨ੑ • ͪΐͬͱॏ͍͚Ͳʜ ؾʹͳΔͱ͜Ζ • ύλʔϯϚονϯά͕ऑ͍ • $BTFDMBTT XIFOʜ • GMBU.BQ ͕ݴޠͱͯ͠΄͍͠ • 4DBMBͷ GPS • &YQFSJNFOUBM͕ຊ౰ʹ࣮ݧత • ίϯύΠϧʹ͕͔͔࣌ؒΔ • LBQU ͕ಛʹॏ͍ • *%&ิ׬΋ॏ͍ جຊతʹ +BWB͔Β৐Γ׵͑ͯ΋ࠔΔ͜ͱ͸ຆͲͳ͍ ,PUMJOͷॻ͖৺஍

Slide 14

Slide 14 text

.FTTBHJOH"1* ͷςετ؀ڥ ,PUMJOLPUFTU Λ࢖ͬͨ &OEUP&OE5FTUJOH*OUFHSBUJPO5FTU

Slide 15

Slide 15 text

talk-server ༑ͩͪ௥Ճ ϝοηʔδૹ৴ MJOFCPUCBDLFOE Chatbot server ϝοηʔδड৴ αʔϏε୯ମͷςετͰ͸ ෳ߹తͳཁҼͰյΕΔ৔߹΋ &OEUP&OEͰ .FTTBHJOH"1*ͷςετΛߦ͍͍ͨ .FTTBHJOH"1*ͷςετ؀ڥ େن໛ͳมߋΛ࠷খݶͷϦεΫͰϦϦʔε͢ΔͨΊʹ͸ɾϚΠΫϩαʔϏεͳΒͰ͸ͷ೰Έ

Slide 16

Slide 16 text

.FTTBHJOH"1*ͷςετ؀ڥ #PU։ൃऀ޲͚ ςετ "1*ͷ 3FRVFTU3FTQPOTFʹมߋ͕ͳ͍͔ .FTTBHJOH"1*ࣗମͷςετɺ8FCIPPLͷड৴ςετ -*/&Ϣʔβʔ ޲͚ςετ ਖ਼͍͠Ϣʔβʔʹϝοηʔδ͕ૹ৴͞ΕΔ͔ɾ಺༰͸ਖ਼͍͔͠ -*/&ϢʔβʔΛٖࣅతʹ࠶ݱͯ͠ςετ UBMLTFSWFS಺෦ "1*ར༻ ಺෦తͳςετ ਖ਼͘͠ૹ৴਺͕ܭ্͞ΕΔ͔ɾ#PUͷઃఆ͕ਖ਼͘͠൓ө͞ΕΔ͔ .FTTBHJOH"1*಺෦ͷ *OUFHSBUJPO5FTU "1*ϓϥοτϑΥʔϜͷςετʹٻΊΒΕΔ΋ͷ

Slide 17

Slide 17 text

1VTI3FQMZ ༑ͩͪ௥Ճ ˠϝοηʔδૹ৴ ˠ 8FCIPPLड৴ ˠ#PUฦ৴ #SPBEDBTU Ϣʔβʔ࡞੒ ˠ ༑ͩͪ௥Ճ ˠϒϩʔυΩϟετ /BSSPXDBTU ಛఆͷଐੑΛ࣋ͭϢʔβʔ࡞੒ ˠφϩʔΩϟετ 8FCIPPL ༷ʑͳλΠϓͷ 8FCIPPLΛݕূ ಈըɾը૾ɾԻ੠ 3JDI.FOV Ϧονϝχϡʔͷηοτ ˠ൓ө֬ೝ ˠVTFS*E ࢦఆͰηοτ (SPVQ3PPN άϧʔϓͷೖୀࣨɾ#PUͷάϧʔϓڐՄͷಈ࡞ɾෳ਺ #PU ରԠ &UD ૯ςετ߲໨͸ Ҏ্ ೥݄ݱࡏ ֎෦޲͚ .FTTBHJOH"1* ͷಈ࡞͔Βɺૹ৴਺؅ཧ౳ɾ಺෦ঢ়ଶɾ৚݅෼ذΛ໢ཏతʹ .FTTBHJOH"1*ςετέʔεͷྫ

Slide 18

Slide 18 text

3FMFBTF1IBTF͝ͱʹςετΛߦ͏ .FTTBHJOH"1*ςετͷର৅ #&5" 3&-&"4& ͦΕͧΕͷ %FQMPZޙʹςετ جຊػೳͷಈ࡞Λ۱ʑ·Ͱࣗಈݕূ ։ൃ؀ڥ SVOTEBZ ຊ൪؀ڥ ݱࡏະରԠ 45"(*/( εςʔδϯά؀ڥ 3$ SVOEBZ ࣗ৴Λ࣋ͬͯຊ൪؀ڥ΁ 8FCIPPL࡮৽࣌ʹ໾ཱͬͨ

Slide 19

Slide 19 text

Ϣʔβʔϝοηʔδड৴ ಺༰ݕূ #PUUP6TFS ϝοηʔδ഑৴ .FTTBHJOH"1* Ϣʔβʔ࡞੒ UBMLTFSWFS .FTTBHJOH"1*ςετͷྲྀΕ -*/&ެࣜΞΧ΢ϯτ ༑ୡొ࿥ -*/&಺෦ "1* 6TFSUP#PU ϝοηʔδૹ৴ -*/&಺෦ "1* 8FCIPPLड৴ ಺༰ݕূ "QQUZQFWFSTJPO ϓϥΠόγʔಉҙͷ༗ແ ϦϓϥΠϝοηʔδ 3FQMZ5PLFO ༗ޮੑݕূ #PUબ୒ɾ࡞੒ ಺෦ "1* (SPVQڐՄ "VUIPSJUZͷ༗ແ ૹ৴਺ݕূ Ϣʔβʔ࡟আ ඇಉظతͳ݁ՌΛݕূ͢Δςετέʔε͕ଟ͍

Slide 20

Slide 20 text

4DBMB5FTU ͷॻ͖৺஍Λ ,PUMJO Ͱ΋࠶ݱͨ͠ςετϑϨʔϜϫʔΫ LPUFTU LPUFTUJP ॊೈͳ 4QFD 4USJOH4QFD 'VO4QFD 4IPVME4QFD ͳͲଟ਺ͷ 4QFD͕༻ҙ͞Ε͍ͯΔ ্खʹ࢖͏ͱ5FTUDBTF͕ߏ଄Խ͠΍͍͢ɾ໊લʹΑͬͯςετ͕؅ཧ͠΍͍͢ ڧྗͳ "TTFSUJPO ,PUMJOOBUJWFͳ "TTFSTUJPO %4-తͳه๏ʹ΋ରԠ /VMMBCMFରԠ΍ &YUFOTJPOGVODUJPO͕ଟ਺༻ҙ͞Ε͍ͯͯॊೈʹॻ͚Δ $PSPVUJOFT "TZOD ରԠ 5FTUNFUIPE͕͢΂ͯ TVTQFOEGVODUJPOʹͳ͍ͬͯΔ "XBJUJMJUZ ͷΑ͏ͳҰఆ࣌ؒ಺ʹ৚݅Λຬͨ͢͜ͱΛ֬ೝ͢Δඇಉظςετ͕ॻ͖΍͍͢

Slide 21

Slide 21 text

ςετͷ؅ཧ ඇಉظରԠ "TTFSUJPOT ॊೈͳ 4QFD ߏ଄Խ͞Εͨςετ $PSPVUJOFTରԠ FWFOUVBMMZ DPOUJOVBMMZ $PODVSSFOUUFTUJOH ,PUMJOOBUJWFͳ GMVFOUBTTFSUJPO 5FTU໊͕Θ͔Γʹ͍͘ ςετΛߏ଄Խ͠ʹ͍͘ SVO#MPDLJOH5FTU ඇಉظςετ೉͍͠ +BWBͷ BTTFSUMJCSBSZ ,PUMJOαϙʔτ͕ෆ׬શ /VMMBCMF౳ +6OJUͱͷൺֱ JUnit 5 kotest

Slide 22

Slide 22 text

࣮ࡍʹ࢖ΘΕ͍ͯΔςετίʔυΛҰ෦ൈਮ LPUFTU ͷαϯϓϧίʔυ class PushTest( … ) : ShouldSpec({ context("/v2/bot/message/push") { should("not push to unrelated user").config(enabledIf = betaOnly) { val text = randomMessageText() val msg = PushMessage(userId, TextMessage(text)) val response = channel.messagingClient.push(msg).awaitResponse() .shouldHaveCode(200) … } } }) BXBJU3FTQPOTF ͸ TVTQFOEGVO TIPVME)BWF$PEF ͸֦ுؔ਺ QVTI ͷฦΓ஋͸ SFUSPGJU3FTQPOTF DPOUFYU ͰςετΛߏ଄Խ ςετ݁Ռͷ໊લ͕Θ͔Γ΍͍͢

Slide 23

Slide 23 text

IUUQTLPUFTUJPEPDTGSBNFXPSLUFTUJOHTUZMFTIUNM LPUFTU ͷ 4QFD 'VO4QFD JOTJQJSFE CZ4DBMB5FTU 4USJOH4QFD "OOPUBUJPO4QFD JOTJQJSFE CZ+6OJU

Slide 24

Slide 24 text

$PSPVUJOFTΛ࢖ͬͨඇಉظ BTTFSUJPO FWFOUVBMMZ ͱ DPOUJOVBMMZ LPUFTU ͷαϯϓϧ eventually(1.minutes) { user.fetchReceivedMessages(channel.primaryBotMid) .forOne { it.message.text.shouldContain(text) // should have proper metadata it.message.contentMetadata.shouldContainExactly(mapOf(…)) } } user.follow(channel.primaryBotMid) continually(20.seconds) { channel.events(seqNo) .filterIsInstance() .forNone { it.source.userId.shouldBe(channel.encrypt(user.mid)) } } ಛఆͷϝοηʔδ͕෼Ҏ಺ʹ͚݅ͩड৴͞ΕΔ ༑ͩͪ௥Ճͯ͠΋ GPMMPXΠϕϯτ͕ඵಧ͔ͳ͍

Slide 25

Slide 25 text

$MVFT ͱ֦ுؔ਺Λ࢖ͬͨ BTTFSUJPOr SFUSPGJUͷΧελϜ BTTFSUJPOͷྫ LPUFTU ͷαϯϓϧ fun retrofit2.Response.shouldHaveCode(expectedCode: Int): retrofit2.Response { asClue { val body = lazy { if (isSuccessful) body() else errorBody()?.string() } withClue(body) { code().shouldBe(expectedCode) } } return this } val msg = PushMessage(userId, TextMessage(text)) val response = channel.messagingClient.push(msg).awaitResponse() .shouldHaveCode(200) org.opentest4j.AssertionFailedError: Response{protocol=h2, code=400, message=, url=https://api.line-beta.me/v2/bot/message/push} {"message":"Failed to send messages"} expected:<200> but was:<400> at com.linecorp.bot.test.common.Response.shouldHaveCode(Response.kt:30) $MVFΛઃఆ 3FTQPOTFͱ CPEZ Τϥʔ࣌ʹ $MVFʹઃఆͨ͠಺༰Λग़ྗ

Slide 26

Slide 26 text

ͦͷଞศརͳػೳ LPUFTU ͷαϯϓϧ should("not push to blocked user") { val user = autoClose(userAccountManager.get()) … } data class UserAccount(…) : AutoCloseable { override fun close() = runBlocking { unregister() } } val contentId = retry(5, 120.seconds, 10.seconds, 1) { client.uploadMessageContent(content) } channel.messagingClient.uploadUserIdList(content) .awaitResponse() .shouldHaveCode(400) .shouldHaveErrorBody { it.string().shouldMatchJson( """{"message":"The request body is missing"}""" ) } BVUP$MPTF 4QFDऴྃ࣌ʹ DMPTF SFUSZ +40/NBUDIFS

Slide 27

Slide 27 text

1SPQFSUZCBTFEUFTUJOH LPUFTU ͷαϯϓϧ

Slide 28

Slide 28 text

"MMVSF'SBNFXPSL΍ 4FOUSZΛར༻ &YUFOTJPOͰςετ݁Ռͷ؂ࢹ ଞνʔϜɾ֎෦ͷγεςϜ͕ෆ҆ఆͳࣄʹΑΔςετࣦഊ͕Α͋͘Δ ಛʹ #&5"؀ڥ ܧଓతʹࣦഊ͍ͯ͠Δςετɾಉ͡໰୊͕ൃੜ͍ͯ͠ΔςετΛޮ཰Α͘؂ࢹ

Slide 29

Slide 29 text

&OEUP&OE5FTUJOH • ࣦഊ͕ଟ͍ ಛʹ#&5" • ৚݅Λ΋ͬͱϥϯμϜʹ͍ͨ͠ • 1SPQFSUZCBTFEUFTUJOH • ࣮ߦ͕࣌ؒ௕Ί ෼ఔ౓ • LPUFTU ͰฒྻԽ • 4FOUSZͷ 5SBDJOHͰ஗͍ ςετΛ؂ࢹ LPUFTU • ࠷৽൛ͷ ,PUMJO͕ඞਢ • &YQFSJNFOUBMػೳΛͨ͘͞Μ࢖͏ • ͨ·ʹόάͬͯΔ • ฒྻςετͱ͔ • .PDLJUP͕ͪΐͬͱ࢖͍ʹ͍͘ • .PDLL Λ࢖͑͹ྑ͍͕ .FTTBHJOH"1*ςετ ࠓޙͷ՝୊ͱؾʹͳΔ఺

Slide 30

Slide 30 text

͍͞͝ʹ 4QSJOH4FDVSJUZ $7&

Slide 31

Slide 31 text

4QSJOH4FDVSJUZ "VUIPSJ[BUJPO#ZQBTTJO3FHFY3FRVFTU.BUDIFS $7& https://tanzu.vmware.com/security/cve-2022-22978 Description In Spring Security versions 5.5.6 and 5.5.7 and older unsupport ed versions, RegexRequestMatcher can easily be misconfigured to be bypassed on some servlet containers. Applications using RegexRequestMatcher with `.` in the regular expression are possibly vulnerable to an authorization bypass. .FTTBHJOH"1*#BDLFOE ։ൃνʔϜ಺෦Ͱൃݟ 7.8BSFʹใࠂ $7&ൃߦ ୈҰൃݟऀ੢໺͞Μʹײँ

Slide 32

Slide 32 text

4QSJOH4FDVSJUZ "VUIPSJ[BUJPO#ZQBTTJO3FHFY3FRVFTU.BUDIFS $7& @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.regexMatcher("/auth/.+"); http.authorizeRequests().anyRequest().authenticated(); http.httpBasic().realmName("test"); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } } ৚݅ • SFHFY.BUDIFS Λར༻ • ΍ ͳͲ Λ࢖͏ • ຤ඌʹվߦίʔυΛೖΕΔͱ ೝূճආ $ curl -v http://localhost:8080/auth/testname # => 401 $ curl -v http://localhost:8080/auth/testname%0a # => 200 !!

Slide 33

Slide 33 text

THANK YOU

Slide 34

Slide 34 text

No content