7.12. 验证

你可能已经发现,程序中有一个严重的安全漏洞:用户可以提供任意的路径在服务器上执行读写操作。为了消除这个问题,我们使用正则表达式验证页面的标题。

首先,添加"regexp"到导入列表。然后创建一个全局变量存储我们的验证正则表达式:

函数regexp.MustCompile解析并且编译正则表达式,返回一个regexp.Regexp对象。和template.MustParseFile类似,当表达式编译错误时,MustCompile抛出一个错误,而Compile在它的第二个返回参数中返回一个os.Error。

现在,我们编写一个函数,它从请求URL解析中解析页面标题,并且使用titleValidator进行验证:

  func getTitle(w http.ResponseWriter, r *http.Request) (title string, err os.Error) {
      title = r.URL.Path[lenPath:]
      if !titleValidator.MatchString(title) {
          http.NotFound(w, r)
          err = os.NewError("Invalid Page Title")
      }
      return
  }

如果标题有效,它返回一个nil错误值。如果无效,它写"404 Not Found"错误到HTTP连接中,并且返回一个错误对象。

修改所有的处理函数,使用getTitle获取页面标题:

  func viewHandler(w http.ResponseWriter, r *http.Request) {
      title, err := getTitle(w, r)
      if err != nil {
          return
      }
      p, err := loadPage(title)
      if err != nil {
          http.Redirect(w, r, "/edit/"+title, http.StatusFound)
          return
      }
      renderTemplate(w, "view", p)
  }

  func editHandler(w http.ResponseWriter, r *http.Request) {
      title, err := getTitle(w, r)
      if err != nil {
          return
      }
      p, err := loadPage(title)
      if err != nil {
          p = &page{title: title}
      }
      renderTemplate(w, "edit", p)
  }

  func saveHandler(w http.ResponseWriter, r *http.Request) {
      title, err := getTitle(w, r)
      if err != nil {
          return
      }
      body := r.FormValue("body")
      p := &page{title: title, body: []byte(body)}
      err = p.save()
      if err != nil {
          http.Error(w, err.String(), http.StatusInternalServerError)
          return
      }
      http.Redirect(w, r, "/view/"+title, http.StatusFound)
  }