const{createApp}=Vue const{createVuetify,useGoTo,useDisplay}=Vuetify var data={alert:{show:false,color:'success',text:'',timeout:0,},theme:{dark:false,},nav:{showDrawer:false,showTOC:true,tocPanel:0,tab:'account',post:{discussionId:6517,currentPage:1,targetPage:1,total:22,anchor:0,goToOptions:{container:null,duration:0,easing:'easeInOutCubic',offset:-100,},worker:null,task:[],active:[],apiLock:[],originLike:new Map([]),},related:{block:1,}},search:{width:80,text:null,loading:false,},tags:[{id:20,url:'/t/2c0b5e546a045c756077023a1a1e',name:'Js|Ts|H5',color:'#FFA726',icon:'mdi-language-javascript',},],posts:[{id:79022,num:0,uid:651,content:'\u003Ccode\u003EDownloading 41.23 MB/41.23 MB, donedependencies:- next 15.1.0+ next 15.2.3Done in 4m 38.6s\u003C/code\u003E\u003Cp\u003ENextJS auth middware 漏洞\u003C/p\u003E漏洞概述\u003Cp\u003E漏洞编号:CVE-2025-29927 / GHSA-f82v-jwr5-mffw漏洞类型:授权绕过( CWE-285 )严重级别:严重( Critical )CVSS 评分:9.1/10.0影响版本:\u003C/p\u003E\u003Cp\u003E11.1.4 至 13.5.614.0 至 14.2.2415.0 至 15.2.2\u003C/p\u003E漏洞简述\u003Cp\u003E此漏洞允许攻击者通过添加特定 HTTP 请求头( x-middleware-subrequest )完全绕过在 Next.js 中间件中实现的授权检查,从而获取对受保护资源的未授权访问。由于许多 Next.js 应用仅在中间件层实现授权逻辑,这使得该漏洞的影响范围极广且危害严重。\u003C/p\u003E\u003Cp\u003E虽然 Vercel 修了但是没有完全修,self host 的 nextjs 用户还是会遇到问题\u003C/p\u003E\u003Cp\u003ECloudflare 和 Vercel CEO 直接推特中门互狙。。。\u003C/p\u003E',ipRegion:'',updatedByUid:0,createdAt:'2025-03-24 21:49:10',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79023,num:1,uid:14506,content:'升级是一方面,另一方面也是个提醒,健壮的 app 应该避免只在一个 layer 做认证和安全保护\u003Cbr\u003Ehttps://strobes.co/blog/understanding-next-js-vulnerability/#Key_Security_Lessons_From_Nextjs_Vulnerability',ipRegion:'',updatedByUid:0,createdAt:'2025-03-24 22:22:10',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79024,num:2,uid:1359,content:'牛批啊 ...',ipRegion:'',updatedByUid:0,createdAt:'2025-03-24 23:37:53',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79025,num:3,uid:4899,content:'nextjs 的 middleware 设计真的文明用语,第一次见只能有一个 middleware 的框架',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 00:04:55',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79026,num:4,uid:10759,content:'https://zhero-web-sec.github.io/research-and-things/nextjs-and-the-corrupt-middleware\u003Cbr\u003E\u003Cbr\u003E看了下漏洞分析...确实瓜皮',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 08:06:19',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79027,num:5,uid:3770,content:'而且是只为 edge runtime 设计的 middleware ,就离谱,github 上有几个 pr 和 discussions 吵这事',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 08:46:32',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79028,num:6,uid:2289,content:'吓死人',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 09:13:01',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79029,num:7,uid:7119,content:'看来我最初的预感是正确的\u003Cbr\u003E最开始用 nextjs 我就觉得这 middleware 不靠谱\u003Cbr\u003E最后还是把 auth 步骤都放到 layout 里\u003Cbr\u003E麻烦是麻烦了点,每个 path 都要写一遍同样的 auth 逻辑,\u003Cbr\u003E但把 cookie 都自己处理了就不必担心这种漏洞了',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 09:28:27',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79030,num:8,uid:2783,content:'twitter 都炸开了。。。也是醉了。。。',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 10:20:03',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79031,num:9,uid:7288,content:'没记错的话 middleware 中的 req/res 和别的接口 req/res 对象还有差异,我是懒得看文档了解什么差异了(毕竟 server 端的 req/res 和客户端的 req/res 也有差异我也懒得看。。。),然后鉴权写到单独的文件里面在各个接口都引了一遍。。。完美规避问题[doge]',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 10:22:59',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79032,num:10,uid:59,content:'我倒是觉得这个设计很不错,模块化中间件,强制使用单个中间件文件,简化配置、防止潜在冲突、优化性能\u003Cbr\u003E\u003Cbr\u003E [官方文档] \u003Cbr\u003ENote: While only one middleware.ts file is supported per project, you can still organize your middleware logic modularly. Break out middleware functionalities into separate .ts or .js files and import them into your main middleware.ts file. This allows for cleaner management of route-specific middleware, aggregated in the middleware.ts for centralized control. By enforcing a single middleware file, it simplifies configuration, prevents potential conflicts, and optimizes performance by **oiding multiple middleware layers.',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 10:24:06',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79033,num:11,uid:4631,content:'不如发推特链接看看',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 10:50:40',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79034,num:12,uid:2682,content:'这世界就是一个草台班子....',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 11:01:26',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79035,num:13,uid:1265,content:'同,想吃瓜 \u003Cimg src\u003D\"https://i.imgur.com/agAJ0Rd.png\"\u003E',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 11:25:47',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79036,num:14,uid:7228,content:'可以追溯到 3 年前的版本了。估计已经产生很多停在这些版本又不维护的项目。乐',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 11:50:20',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79037,num:15,uid:14507,content:'从来不用 middleware 鉴权,主要是不用的页面,有的需要鉴权,有的不需要鉴权,如果按不同页面写鉴权策略写到一个 middleware 逻辑太离散了,所以直接写到 page 里面了,都是每个页面都得粘贴一下有点麻烦。',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 11:54:08',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79038,num:16,uid:5064,content:'https://x.com/eastdakota/status/1903574608789274748',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 11:58:36',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79039,num:17,uid:14508,content:'目前用 nextjs 写网站都只用 output: export 生成静态页面,应该遇不到这种问题',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 14:53:48',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79040,num:18,uid:581,content:'23 号开始吵架,今天才开始道歉,哈哈哈哈',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 16:21:52',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},{id:79041,num:19,uid:14509,content:'确实**,用到我吐血',ipRegion:'',updatedByUid:0,createdAt:'2025-03-25 17:43:29',updatedAt:'2025-03-27 11:47:03',mentionNum:0,mentionedBy:[],mentionUsers:[],likeUsers:[],},],usersMap:new Map([[5064,{uid:5064,url:'/u/002141376a045c72606c526f1b013411',avatar:'/a/002141376a045c72606c526f1b013411',username:'CHTuring🤖',}],[4899,{uid:4899,url:'/u/2f7958556a045c7368635f6f102c072c',avatar:'/a/2f7958556a045c7368635f6f102c072c',username:'ragnaroks🤖',}],[3770,{uid:3770,url:'/u/1e2f79036a045c74676d566f1550290e',avatar:'/a/1e2f79036a045c74676d566f1550290e',username:'cat🤖',}],[14509,{uid:14509,url:'/u/6b715f3f6a045d73656a5f6f005d1f0b',avatar:'/a/6b715f3f6a045d73656a5f6f005d1f0b',username:'kamilic🤖',}],[59,{uid:59,url:'/u/111d060f6a045c77606f5f6f403d3905',avatar:'/a/111d060f6a045c77606f5f6f403d3905',username:'kk2syc🤖',}],[2783,{uid:2783,url:'/u/393905236a045c756762556f3058361d',avatar:'/a/393905236a045c756762556f3058361d',username:'qweruiop🤖',}],[7228,{uid:7228,url:'/u/133077346a045c7062685e6f430b0b0a',avatar:'/a/133077346a045c7062685e6f430b0b0a',username:'zealotxxxx🤖',}],[2289,{uid:2289,url:'/u/2825713c6a045c7562625f6f07193f21',avatar:'/a/2825713c6a045c7562625f6f07193f21',username:'guhuisec🤖',}],[581,{uid:581,url:'/u/6e1d580b6a045c776562576f332f6b08',avatar:'/a/6e1d580b6a045c776562576f332f6b08',username:'shunia🤖',}],[14507,{uid:14507,url:'/u/351d53196a045d73656a516f182d0433',avatar:'/a/351d53196a045d73656a516f182d0433',username:'jimages🤖',}],[4631,{uid:4631,url:'/u/0f1058086a045c736669576f41230670',avatar:'/a/0f1058086a045c736669576f41230670',username:'0x676e67🤖',}],[651,{uid:651,url:'/u/113c615e6a045c77666f576f16030134',avatar:'/a/113c615e6a045c77666f576f16030134',username:'Oxonomy🤖',}],[7119,{uid:7119,url:'/u/6270001e6a045c70616b5f6f390e3c2c',avatar:'/a/6270001e6a045c70616b5f6f390e3c2c',username:'NessajCN🤖',}],[2682,{uid:2682,url:'/u/2c2f410c6a045c756662546f42181b24',avatar:'/a/2c2f410c6a045c756662546f42181b24',username:'zhwguest🤖',}],[1359,{uid:1359,url:'/u/1803741d6a045c76636f5f6f4e5a3211',avatar:'/a/1803741d6a045c76636f5f6f4e5a3211',username:'lizhenda🤖',}],[10759,{uid:10759,url:'/u/35390b176a045d77676f5f6f3003100b',avatar:'/a/35390b176a045d77676f5f6f3003100b',username:'flyqie🤖',}],[14508,{uid:14508,url:'/u/391d61226a045d73656a5e6f062d2929',avatar:'/a/391d61226a045d73656a5e6f062d2929',username:'KellyAlsa🤖',}],[7288,{uid:7288,url:'/u/3d2775186a045c7062625e6f06111975',avatar:'/a/3d2775186a045c7062625e6f06111975',username:'Xheldon🤖',}],[1265,{uid:1265,url:'/u/367a7f076a045c76626c536f25136b1d',avatar:'/a/367a7f076a045c76626c536f25136b1d',username:'Zenyet🤖',}],[14506,{uid:14506,url:'/u/30075a036a045d73656a506f2d06120b',avatar:'/a/30075a036a045d73656a506f2d06120b',username:'x**ierchow🤖',}],]),related:[{title:'推荐一款自己写的提升效率的浏览器插件: HarmonyAutoCopy,让文本复制更轻松!',url:'/d/6f06033d6a045c77606a567a475c646a1f371708',},{title:'程序猿创业失败,转行做独立开发',url:'/d/393e7d3e6a045c77606a56754350616a59191c16',},{title:'发现一个有意思的项目,把一个字符串隐藏到另一个字符串中',url:'/d/3c117a346a045c77606a56754359676a5b323b0b',},{title:'编程小白,终于成功上线了自己的第一个导航站😭 激动',url:'/d/333944256a045c77606a56754451666a3e0e3e67',},{title:'vue 指令更新问题',url:'/d/2f3c4a3e6a045c77606a5675445e636a250e3f24',},{title:'小红书 | 图文,视频,评论 浏览与导出工具',url:'/d/1b1806556a045c77606a56754458676a04343b26',},{title:'[BadouCMS] Vue3+TypeScript+ThinkPHP8 构建 CMS 系统(开源)',url:'/d/303350036a045c77606a56754458626a20390564',},{title:'开发了一个开箱即用的本地终端 Ztty',url:'/d/0c045a2e6a045c77606a5675455e6b6a3e200734',},{title:'第一个完全用 AI 工具生成的工具站',url:'/d/6a18771a6a045c77606a5675475e616a01126104',},{title:'为什么 vue 的 nuxt.js 不跟进 nextjs 的 app route 目录结构',url:'/d/02116a1e6a045c77606a56744f516a6a1b02141e',},{title:'Vite 开发服务器路径遍历漏洞(CVE-2025-30208)',url:'/d/2f2f0a2b6a045c77606a56744f5a646a231a021b',},{title:'做定时任务,一定要用这个神库!',url:'/d/2a1a550b6a045c77606a56744f5b626a5c151613',},{title:'XUGOU - 轻量级系统监控平台,基于 CloudFlare 零成本部署!',url:'/d/0a21430f6a045c77606a56744f58656a0f741e25',},{title:'Typescript 如此成功,为何没有发展出所谓 “Typthon”?',url:'/d/35315c016a045c77606a5674445f626a0e730224',},{title:'为什么 Python 、Node.js 就不能学习一下 C#这种优雅的依赖管理方式?',url:'/d/0f1f5f3d6a045c77606a5674445c676a04311f60',},{title:'求助!双显卡连接 6 个屏幕,展示不同的 URL 页面。Electron 的 displayId 每次重启都会变',url:'/d/1f3158176a045c77606a56744551676a24111b3b',},{title:'关于 react native 和 flutter',url:'/d/6f7c03286a045c77606a56774e50616a27221935',},{title:'[野生程序员花三天时间用 Cursor 复刻经典游戏「俄罗丝方块」,求各位提点建议]',url:'/d/6b047a5e6a045c77606a56774e5c626a2b361d65',},{title:'分享一个用 JS、canvas 写的星空穿越效果',url:'/d/191c61346a045c77606a5677425e6a6a2f28273c',},{title:'郑州招 Python , Node.js 开发岗',url:'/d/082c61146a045c77606a5677425e616a3d706116',},],} const App={setup(){const goTo=useGoTo() const{mdAndUp}=useDisplay() return{goTo,mdAndUp}},data(){return data;},mounted(){const themeDark=localStorage.getItem("themeDark") if(themeDark!==null){this.theme.dark=JSON.parse(themeDark)} if(this.nav.post.total>(this.nav.post.currentPage-1)*100+20){let moreLen=100 if(this.nav.post.total({id:null,num:(this.nav.post.currentPage-1)*100+v,uid:null,content:null,ipRegion:null,updatedByUid:null,createdAt:null,updatedAt:null,mentionNum:null,mentionedBy:null,mentionUsers:null,likeUsers:null,})) this.posts.push(...morePosts.slice(20))} this.workerStart() const hash=window.location.hash const match=hash.match(/#(\d+)/) if(match){const n=parseInt(match[1],10) if(n>=(this.nav.post.currentPage-1)*100&&n{this.jumpTo(n)})}} this.$nextTick(()=>{this.addHeadingIds() tocbot.init({tocSelector:'.toc',contentSelector:'#post-content-0',headingSelector:'h2, h3, h4',headingsOffset:100,scrollSmoothOffset:-100,scrollSmooth:true,collapseDepth:6,onClick:function(e){setTimeout(()=>{history.replaceState(null,'',window.location.pathname+window.location.search)},0)},}) tocbot.refresh()});},beforeUnmount(){this.workerStop() if(this.quill){this.quill.destroy() this.quill=null}},computed:{dposts(){return this.posts.slice(20);},},created(){},methods:{successAlert(msg){this.alert={show:true,color:'success',text:msg,timeout:1500,}},failureAlert(msg){this.alert={show:true,color:'error',text:msg,timeout:5000,}},flipThemeDark(){this.theme.dark=!this.theme.dark localStorage.setItem("themeDark",JSON.stringify(this.theme.dark))},toSearch(){if(!this.search.text){this.failureAlert('搜索词不能为空') return} let keywords=this.search.text.trim() if(keywords.length<1){this.failureAlert('搜索词不能为空') return} if(keywords.length>100){this.failureAlert('搜索词过长') return} this.doSearch(keywords)},toReg(){window.location.href="/reg"},toLogin(){window.location.href="/login"},toPage(){let url=window.location.href url=url.replace(/(\/\d+)?(#[0-9]+)?$/,this.nav.post.targetPage>1?`/${this.nav.post.targetPage}`:'') window.location.href=url},toLoadRelated({done}){if(this.my&&this.my.uid){this.apiLoadRelated({done})}else{done('ok')}},workerStart(){this.nav.post.worker=setInterval(()=>{this.workerLoad()},500);},workerStop(){if(this.nav.post.worker){clearInterval(this.nav.post.worker);this.nav.post.worker=null;}},async jumpTo(num){const page=Math.floor(num/100)+1 const i=num-(page-1)*100 if(page===this.nav.post.currentPage){this.goTo("#post-"+num,this.nav.post.goToOptions) if(!this.posts[i].id){const block=Math.floor(num/20)+1 this.nav.post.apiLock[block]=true await this.apiLoadPosts(block) this.$nextTick(()=>{this.goTo("#post-"+num,this.nav.post.goToOptions)})}}else{let url=window.location.href url=url.replace(/(\/\d+)?(#[0-9]+)?$/,page>1?`/${page}`:'') url=url+"#"+num window.location.href=url}},postIntersect(num){return(isIntersecting,entries,observer)=>{if(isIntersecting){this.nav.post.task.push(num) this.nav.post.active.push(num) this.nav.post.active=this.nav.post.active.filter(item=>Math.abs(item-num)<=5) this.nav.post.active.sort((a,b)=>a-b)}else{this.nav.post.active=this.nav.post.active.filter(item=>item!==num)} if(this.nav.post.active[0]){this.nav.post.anchor=this.nav.post.active[0]}else{this.nav.post.anchor=0}}},async apiLoadPosts(block){try{const response=await axios.post('/fapi/v1/post/block/'+block,{discussionId:this.nav.post.discussionId,}) if(response.data.code===0){response.data.data.posts.forEach(post=>{const i=post.num%100 Object.assign(this.posts[i],post)}) response.data.data.users.forEach(user=>{this.usersMap.set(user.uid,user)})}else{this.failureAlert('回帖数据加载失败: '+response.data.msg)}}catch(error){this.failureAlert('回帖数据加载失败: '+error)} this.nav.post.apiLock[block]=false},workerLoad(){while(this.nav.post.task.length){const num=this.nav.post.task.pop() const i=num-(this.nav.post.currentPage-1)*100 if(!this.posts[i].id){const block=Math.floor(num/20)+1 if(!this.nav.post.apiLock[block]){this.nav.post.apiLock[block]=true this.apiLoadPosts(block)}}}},getTimeInfo(t){if(!t){return ""} const now=new Date();const then=new Date(t);const diff=now-then;const minute=60*1000;const hour=minute*60;const day=hour*24;const month=day*30;const year=month*12;if(diffpost.num===num) if(!post){return "#"+num} const uid=post.uid const username=this.usersMap.get(uid)?.username if(!username){return "#"+num} return username},getUsernameByPostId(id){const post=this.posts.find(post=>post.id===id) if(!post){return "#"+this.getPostNumByPostId(id)} const uid=post.uid const username=this.usersMap.get(uid).username if(!username){return "#"+this.getPostNumByPostId(id)} return username},getPostNumByPostId(id){const post=this.posts.find(post=>post.id===id) return post.num},getPostById(id){const post=this.posts.find(post=>post.id===id) return post},getPostByNum(num){const post=this.posts.find(post=>post.num===num) return post},getAvatarByUid(uid){const avatar=this.usersMap.get(uid)?.avatar if(!avatar){return this.getRandomAvatar()} return avatar},getAvatarByPostNum(num){const post=this.posts.find(post=>post.num===num) if(!post){return this.getRandomAvatar()} const uid=post.uid return this.getAvatarByUid(uid)},getRandomAvatar(){const num=Math.floor(Math.random()*100) return "https://randomuser.me/api/portraits/men/"+num+".jpg"},getUrlByUid(uid){const url=this.usersMap.get(uid)?.url if(!url){return ""} return url},getTextByPostNum(num){const post=this.posts.find(post=>post.num===num) if(!post||!post.content){return '点击跳转到#'+num+'查看'} const parser=new DOMParser() const doc=parser.parseFromString(post.content,'text/html') const text=doc.body.textContent||'' return text.slice(0,100)},addHeadingIds(){const content=document.getElementById('post-content-0') if(!content){this.nav.showTOC=false return} const headings=content.querySelectorAll('h2, h3, h4') headings.forEach((heading,index)=>{if(!heading.id){heading.id=`toc-nav-${index}`}}) if(headings.length==0){this.nav.showTOC=false}},async doSearch(keywords){this.search.loading=true try{const response=await axios.post('/fapi/v1/search',{keywords:keywords,}) if(response.data.code===0){if(response.data.data.hash&&response.data.data.hash.length===32){window.location.href="/s/"+response.data.data.hash}else{this.failureAlert('搜索失败: 搜索服务异常')}}else{this.failureAlert('搜索失败: '+response.data.msg)}}catch(error){this.failureAlert('搜索失败: '+error)} this.search.loading=false},debounce(fn,delay){let timer=null return function(...args){if(timer)clearTimeout(timer) timer=setTimeout(()=>{fn.apply(this,args)},delay);};},},watch:{'nav.post.targetPage':{handler:async function(newV,oldV){this.toPage()},immediate:false},},} const vuetify=createVuetify({defaults:{global:{ripple:true,},},}) const app=createApp(App) app.use(vuetify).mount("#app")