Enforcement of proper encapsulation in coding practices
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type User struct {
PublicID string
Username string
Password string
}
func main() {
r := gin.Default()
r.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
user := User{id, "test", "test123"}
c.JSON(200, user)
})
r.Run()
}
The above code defines a User struct with all fields as public. In Go, a variable is exported (public) if it begins with a capital letter. In this case,
PublicID
,
Username
, and
Password
are all public fields.
This is a security vulnerability because it violates the principle of encapsulation. Encapsulation is one of the fundamental concepts in object-oriented programming (OOP). It describes the bundling of data with the methods that operate on that data.
In this case, by making the
Password
field public, we are potentially exposing sensitive data. Anyone with an instance of the User struct can read or modify the password.
Moreover, this violates the principle of least privilege, which states that code should only have the permissions it needs to perform its task and no more. By making the
Password
field public, we are giving more permissions than necessary.
This inappropriate coding practice can lead to data leakage or unauthorized data modification if the struct is used inappropriately. Therefore, it is a good practice to always encapsulate your data and provide public getter and setter methods to access and modify the data.
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type User struct {
privateID string
username string
password string
}
func (u *User) GetID() string {
return u.privateID
}
func (u *User) GetUsername() string {
return u.username
}
func (u *User) GetPassword() string {
return u.password
}
func (u *User) SetID(id string) {
u.privateID = id
}
func (u *User) SetUsername(username string) {
u.username = username
}
func (u *User) SetPassword(password string) {
u.password = password
}
func main() {
r := gin.Default()
r.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
user := User{}
user.SetID(id)
user.SetUsername("test")
user.SetPassword("test123")
c.JSON(200, gin.H{
"id": user.GetID(),
"username": user.GetUsername(),
"password": user.GetPassword(),
})
})
r.Run()
}
The original code had a vulnerability because it did not respect the encapsulation principle. The fields of the
User
struct were public, which means they could be accessed and modified directly from outside the struct. This could lead to unintended side effects and make the code harder to maintain.
The fixed code respects the encapsulation principle by making the fields of the
User
struct private and providing getter and setter methods to access and modify these fields. This way, the fields can only be accessed and modified through these methods, which gives us more control over what can be done with them.
In the
main
function, instead of creating a
User
with public fields, we create an empty
User
and use the setter methods to set the fields. When we send the user as a JSON response, we use the getter methods to get the fields.
This fix improves the security and maintainability of the code by ensuring that the fields of the
User
struct can only be accessed and modified in controlled ways.